March 21, 2007

Bill Baxter wrote:
> Andrei Alexandrescu (See Website For Email) wrote:
>> Don Clugston wrote:
>>> Andrei Alexandrescu (See Website For Email) wrote:
>>>> A while ago, C++ did the mistake of defining std::numeric_limits<T>::min() with a different semantics for floating-point types than for integral types. That hurt generic numeric code a lot.
>>>>
>>>> D has taken over the same mistake: T.min means the smallest value of the type, except for floating-point types, where it means the smallest positive value.
>>>>
>>>> The right way is to have T.min always return the minimum value (duh) and define a separate property T.min_positive.
>>>>
>>>> The question is, would a lot of code be hurt by such a change?
>>>>
>>>>
>>>> Andrei
>>>
>>> It probably wouldn't break a huge amount of D code, but I don't think there would be many cases where T.min for a floating point type would be useful. More significant is the problems involved in converting from C or Fortran code to D.
>>
>> I'm not even discussing the utility of min_positive. All I'm saying is that if you say "min", you should return "min", particularly when others do exactly that.
>>
>>> On a more profound level...
>>> I'm not aware of many cases where it's possible to treat integer and
>>> floating-points generically. People often try, but usually the code
>>> is incorrect for the floating point types, since the semantics are
>>> completely different. (For example, I don't know why x++ is legal for
>>> floating point types; I think it's just a newbie trap; you have no
>>> guarantee that x++ is different to x).
>>>
>>> What type of generic numeric code did you have in mind? What are the benefits which would come by such a change?
>>
>> Trivially simple: the min and max functions. For min, the code picks the type with the smallest .min. For max, the code picks the type with the largest .max.
>>
>>
>> Andrei
> 
> 
> Also when you're say trying to find the maximum of a set of numbers it can be handy to initialize the 'current_max' to the smallest number possible.
> 
> float max_val = float.min; // want the new meaning here
> int max_idx = -1;
> foreach(i,x; bunch_o_floats) {
>    if (x<max_val) {
>        max_val=x;
>        max_idx=i;
>    }
> }
> 
> --bb

I usually cheat and use nan, then change the comparison so that it will succeed if 'x' is any real number :P

That way, if I give it an empty list, I get nan back instead of float.min, which could be misleading.

	-- Daniel

-- 
int getRandomNumber()
{
    return 4; // chosen by fair dice roll.
              // guaranteed to be random.
}

http://xkcd.com/

v2sw5+8Yhw5ln4+5pr6OFPma8u6+7Lw4Tm6+7l6+7D i28a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP  http://hackerkey.com/
March 21, 2007
Daniel Keep wrote:
> 
> Bill Baxter wrote:
>> Also when you're say trying to find the maximum of a set of numbers it
>> can be handy to initialize the 'current_max' to the smallest number
>> possible.
>>
>> float max_val = float.min; // want the new meaning here
>> int max_idx = -1;
>> foreach(i,x; bunch_o_floats) {
>>    if (x<max_val) {
>>        max_val=x;
>>        max_idx=i;
>>    }
>> }
>>
>> --bb
> 
> I usually cheat and use nan, then change the comparison so that it will
> succeed if 'x' is any real number :P
> 
> That way, if I give it an empty list, I get nan back instead of
> float.min, which could be misleading.

I'd use -float.infinity as the initial value. Empty quantifications should return the identity value[1]. At least, that's what I was taught in "Logic and Set Theory" (not sure if I translated that course name right) as well as several other courses I can't remember the names of right now.
You'd still need to make sure your comparison does the right thing for NaNs, if that's important. (I'm pretty sure they didn't cover NaNs in any course :) )


[1]: The identity value of an operator <op> is the value e such that (for all x) e <op> x == x <op> e == x holds. (i.e. for '+' it's 0, for '*' it's 1, and for 'max' it's -infinity)
March 21, 2007
0ffh wrote:
> Don Clugston wrote:
>> completely different. (For example, I don't know why x++ is legal for floating point types; I think it's just a newbie trap; you have no guarantee that x++ is different to x).
> 
> Nope, not at all.
> Standard C defines that after "x++" x is incremented by one, exactly -
> *even for fp types* !

For floating point types, if x happens to be +-infinity then x+1==x.
March 21, 2007
Frits van Bommel wrote:
> Daniel Keep wrote:
>>
>> Bill Baxter wrote:
>>> Also when you're say trying to find the maximum of a set of numbers it
>>> can be handy to initialize the 'current_max' to the smallest number
>>> possible.
>>>
>>> float max_val = float.min; // want the new meaning here
>>> int max_idx = -1;
>>> foreach(i,x; bunch_o_floats) {
>>>    if (x<max_val) {
>>>        max_val=x;
>>>        max_idx=i;
>>>    }
>>> }
>>>
>>> --bb
>>
>> I usually cheat and use nan, then change the comparison so that it will
>> succeed if 'x' is any real number :P
>>
>> That way, if I give it an empty list, I get nan back instead of
>> float.min, which could be misleading.
> 
> I'd use -float.infinity as the initial value. 

Yeh, I guess that's better.  I've just gotten used to working with languages where there's no portable way to get at nan and infinity constants.  But I guess D doesn't have that problem.  Yay D!

--bb
March 21, 2007
Walter Bright wrote:
> 0ffh wrote:
>> Don Clugston wrote:
>>> completely different. (For example, I don't know why x++ is legal for floating point types; I think it's just a newbie trap; you have no guarantee that x++ is different to x).
>>
>> Nope, not at all.
>> Standard C defines that after "x++" x is incremented by one, exactly -
>> *even for fp types* !
> 
> For floating point types, if x happens to be +-infinity then x+1==x.

Or, most likely, even if x happens to be 1E100.  I've not had the pleasure of working on hardware which supported floating point accurate enough to distinguish 1E100 from 1+1E100.

-- James

March 21, 2007
James Dennett wrote:
> Walter Bright wrote:
>> 0ffh wrote:
>>> Don Clugston wrote:
>>>> completely different. (For example, I don't know why x++ is legal for
>>>> floating point types; I think it's just a newbie trap; you have no
>>>> guarantee that x++ is different to x).
>>> Nope, not at all.
>>> Standard C defines that after "x++" x is incremented by one, exactly -
>>> *even for fp types* !
>> For floating point types, if x happens to be +-infinity then x+1==x.
> 
> Or, most likely, even if x happens to be 1E100.  I've not
> had the pleasure of working on hardware which supported
> floating point accurate enough to distinguish 1E100 from
> 1+1E100.
> 
> -- James
> 

The cutoff is basically 2^{mantissa bits+1}.
With floats:  2^23 + 1 == 2^23
With double:  2^53 + 1 == 2^53

So it happens for values much less than 1e100.  More like 1e7 and 1e16.

--bb
March 21, 2007
Bill Baxter wrote:
> James Dennett wrote:
>> Walter Bright wrote:
>>> 0ffh wrote:
>>>> Don Clugston wrote:
>>>>> completely different. (For example, I don't know why x++ is legal for
>>>>> floating point types; I think it's just a newbie trap; you have no
>>>>> guarantee that x++ is different to x).
>>>> Nope, not at all.
>>>> Standard C defines that after "x++" x is incremented by one, exactly -
>>>> *even for fp types* !
>>> For floating point types, if x happens to be +-infinity then x+1==x.
>>
>> Or, most likely, even if x happens to be 1E100.  I've not
>> had the pleasure of working on hardware which supported
>> floating point accurate enough to distinguish 1E100 from
>> 1+1E100.
>>
>> -- James
>>
> 
> The cutoff is basically 2^{mantissa bits+1}.
> With floats:  2^23 + 1 == 2^23

doh! typo -- float has 23 mantissa bits so that should be 24.

> With double:  2^53 + 1 == 2^53
> 
> So it happens for values much less than 1e100.  More like 1e7 and 1e16.

--bb
March 21, 2007
Walter Bright wrote:
> 0ffh wrote:
>> Don Clugston wrote:
>>> completely different. (For example, I don't know why x++ is legal for floating point types; I think it's just a newbie trap; you have no guarantee that x++ is different to x).
>>
>> Nope, not at all.
>> Standard C defines that after "x++" x is incremented by one, exactly -
>> *even for fp types* !
> 
> For floating point types, if x happens to be +-infinity then x+1==x.

Wow, that's almost correct transfinite arithmetic.  Technically speaking, 1 + oo == oo, but oo + 1 != oo.  IEEE should really fix that. ;>

Dave
March 21, 2007
0ffh wrote:
> 0ffh wrote:
>> Standard C defines that after "x++" x is incremented by one, exactly -
> 
> Sorry for self-reply!
> Of course if you have a *very* big number, that might mean it's the same.... sometimes I do faster typing than thinking... :)
> 

Exactly -- that's the trap.
Also it's not reversible.
x++;
x--;
changes the value of x in many cases:

Old x          New x
real.max       real.infinity  // overflow
1.2345678e-10  1.2e-10        // loss of precision
1e-50          0              // catastrophic cancellation
March 21, 2007
Andrei Alexandrescu (See Website For Email) wrote:
> Don Clugston wrote:
>> Andrei Alexandrescu (See Website For Email) wrote:
>>> A while ago, C++ did the mistake of defining std::numeric_limits<T>::min() with a different semantics for floating-point types than for integral types. That hurt generic numeric code a lot.
>>>
>>> D has taken over the same mistake: T.min means the smallest value of the type, except for floating-point types, where it means the smallest positive value.
>>>
>>> The right way is to have T.min always return the minimum value (duh) and define a separate property T.min_positive.
>>>
>>> The question is, would a lot of code be hurt by such a change?
>>>
>>>
>>> Andrei
>>
>> It probably wouldn't break a huge amount of D code, but I don't think there would be many cases where T.min for a floating point type would be useful. More significant is the problems involved in converting from C or Fortran code to D.
> 
> I'm not even discussing the utility of min_positive. All I'm saying is that if you say "min", you should return "min", particularly when others do exactly that.
> 
>> On a more profound level...
>> I'm not aware of many cases where it's possible to treat integer and floating-points generically. People often try, but usually the code is incorrect for the floating point types, since the semantics are completely different. (For example, I don't know why x++ is legal for floating point types; I think it's just a newbie trap; you have no guarantee that x++ is different to x).
>>
>> What type of generic numeric code did you have in mind? What are the benefits which would come by such a change?
> 
> Trivially simple: the min and max functions. For min, the code picks the type with the smallest .min. For max, the code picks the type with the largest .max.

Obviously, but are there many other functions like that? Also, you really should treat floating point as a special case, anyway, because of the possibility of a NaN.

I'd be surprised if the total benefit amounted to more than a few dozen lines of library code.

There's also the issue of complex types. Currently, this passes:

static assert(creal.max == real.max+1i*real.max);

-- which is a little strange.