November 05, 2009
Joel Christensen wrote:
>> To be safe, whenever converting to int, always add a small epsilon.  I think you can use float.epsilon, but I don't have any experience with whether that is always reasonable.
>>
>> -Steve
> 
> Thanks every one for the replies.
> 
> I still have problems. How do I use epsilon? 'real' helps in my example program but not in my money program. I think there's a function out there that does dollars and cents (eg. .89 -> 89c and 1.00 -> $1), but I'm interested how to solve this problem.


If the values you are working with are monetary, I don't think it makes sense to convert to int by casting, since casting always rounds towards zero. Instead you should use the std.math.lround() function, which rounds in a more natural way (towards the nearest integer, away from zero if the fractional part is exactly 0.5).

To answer your question, this is how you would get and use the epsilon value in your example:

  cast(int)(f*100f + float.epsilon);

-Lars
November 05, 2009
"Lars T. Kyllingstad" <public@kyllingen.NOSPAMnet> wrote:

> Charles Hixson wrote:
>> Lars T. Kyllingstad wrote:
>>> Michal Minich wrote:
>>>> Hello rmcguire,
>>>>
>>>>> why is this not a compiler bug?
>>>>> because:
>>>>> import std.stdio;
>>>>> void main() {
>>>>> float f=0.01;
>>>>> writefln("%0.2f->%d",f,cast(int)(f*100f));
>>>>> writefln("%0.2f->%d",f,cast(int)(.01*100f));
>>>>> writefln("%0.2f->%f",f,(f*100f));
>>>>> }
>>>>> results in:
>>>>> 0.01->0
>>>>> 0.01->1
>>>>> 0.01->1.000000
>>>>> I would say something is dodgy.
>>>>>
>>>>> -Rory
>>>>>
>>>>
>>>> I think this may be case of:
>>>> At comple time floating point computations may be done at a higher
>>>> precision than run time.
>>>
>>>
>>> Yes, if you do this:
>>>
>>> float f = 0.01;
>>> float g = f * 100f;
>>> real r = f * 100f;
>>> writeln("%s, %s, %s", f, cast(int) g, cast(int) r);
>>>
>>> you get:
>>>
>>> 0.01, 0, 1
>>>
>>> I believe just writing cast(int)(f*100f) is more or less the same as the
>>> 'real' case above.
>>>
>>> -Lars
>> Can that *really* be the explanation??  I know that float doesn't have all that much precision, but I thought it was more than 5 or 6 places...and this is, essentially, two places.
> 
> 
> Yes it does, but that's not what matters.
> 
> Say that for floats, the representable number closest to 0.01 is 0.01000000000001. (This is just an example, I don't know the true number.) Then you have a lot more precision than the two digits you mention, and multiplying with 100 gives 1.000000000001. Round this towards zero (which is what cast(int) does) and you get 1. This is the 'float g = f*100f;' case in my example.
> 
> Now, say that for reals, which (I think) is what the compiler uses
> internally, the number closest to 0.01 is
>      0.0099999999999999999999999999999999999999.
> Again, just an example, the point is that the precision is higher than
> the above, but the closest number is now smaller than 0.01. Multiply
> this by 100, and you get
>      0.99999999999999999999999999999999999999.
> This number will be cast to the integer 0, which happens in the OP's case.
> 
> I admit I'm no expert in these things, but I suspect this is how it goes. By the way, I recommend Don's excellent article on floating-point numbers. It has really cleared things up for me:
> 
>    http://www.digitalmars.com/d/2.0/d-floating-point.html
> 
> -Lars
> 

Hmm, thanks.

pity its not consistent. Would be nice if you didn't have to learn everything about floating point just to make a calculator.

-Rory

November 05, 2009
rmcguire wrote:
> "Lars T. Kyllingstad" <public@kyllingen.NOSPAMnet> wrote:
>  
>> Charles Hixson wrote:
>>> Lars T. Kyllingstad wrote:
>>>> Michal Minich wrote:
>>>>> Hello rmcguire,
>>>>>
>>>>>> why is this not a compiler bug?
>>>>>> because:
>>>>>> import std.stdio;
>>>>>> void main() {
>>>>>> float f=0.01;
>>>>>> writefln("%0.2f->%d",f,cast(int)(f*100f));
>>>>>> writefln("%0.2f->%d",f,cast(int)(.01*100f));
>>>>>> writefln("%0.2f->%f",f,(f*100f));
>>>>>> }
>>>>>> results in:
>>>>>> 0.01->0
>>>>>> 0.01->1
>>>>>> 0.01->1.000000
>>>>>> I would say something is dodgy.
>>>>>>
>>>>>> -Rory
>>>>>>
>>>>> I think this may be case of:
>>>>> At comple time floating point computations may be done at a higher
>>>>> precision than run time.
>>>>
>>>> Yes, if you do this:
>>>>
>>>> float f = 0.01;
>>>> float g = f * 100f;
>>>> real r = f * 100f;
>>>> writeln("%s, %s, %s", f, cast(int) g, cast(int) r);
>>>>
>>>> you get:
>>>>
>>>> 0.01, 0, 1
>>>>
>>>> I believe just writing cast(int)(f*100f) is more or less the same as the
>>>> 'real' case above.
>>>>
>>>> -Lars
>>> Can that *really* be the explanation??  I know that float doesn't have all that much precision, but I thought it was more than 5 or 6 places...and this is, essentially, two places.
>>
>> Yes it does, but that's not what matters.
>>
>> Say that for floats, the representable number closest to 0.01 is 0.01000000000001. (This is just an example, I don't know the true number.) Then you have a lot more precision than the two digits you mention, and multiplying with 100 gives 1.000000000001. Round this towards zero (which is what cast(int) does) and you get 1. This is the 'float g = f*100f;' case in my example.
>>
>> Now, say that for reals, which (I think) is what the compiler uses internally, the number closest to 0.01 is
>>      0.0099999999999999999999999999999999999999.
>> Again, just an example, the point is that the precision is higher than the above, but the closest number is now smaller than 0.01. Multiply this by 100, and you get
>>      0.99999999999999999999999999999999999999.
>> This number will be cast to the integer 0, which happens in the OP's case.
>>
>> I admit I'm no expert in these things, but I suspect this is how it goes. By the way, I recommend Don's excellent article on floating-point numbers. It has really cleared things up for me:
>>
>>    http://www.digitalmars.com/d/2.0/d-floating-point.html
>>
>> -Lars
>>
> 
> Hmm, thanks.
> 
> pity its not consistent. Would be nice if you didn't have to learn everything about floating point just to make a calculator.


You don't, really.  You just stay away from casts. :)  IMHO, casts are only good for hammering a value of one type into a different type, no worries about the result.

I (almost) never use cast(int) to round floating-point numbers.  There are two reasons for this:

  1. It always rounds towards zero, which is usually not what I want.
  2. It doesn't perform any checks, and can easily overflow.

If the rounding mode is not important, or if for some reason I want the same result as cast(int), I use std.conv.to().  This function checks that the converted number fits in the target type and then performs an ordinary cast.  (Note: D2 only!)

  import std.conv;

  void main()
  {
      int i = to!int(1.23);     // OK
      assert (i == 1);

      int j = to!int(real.max); // ConvOverflowError
  }

If "natural" rounding, i.e. towards nearest integer, is what I want, then I use std.math.lround() or std.math.round().  There are a lot of rounding functions in std.math that do different things and that are worth checking out.

  import std.math;

  void main()
  {
      long i = lround(1.23);
      assert (i == 1);

      long j = lround(0.5);
      assert (j == 1);
  }

-Lars
November 05, 2009
"Lars T. Kyllingstad" <public@kyllingen.NOSPAMnet> wrote:

> rmcguire wrote:
>> "Lars T. Kyllingstad" <public@kyllingen.NOSPAMnet> wrote:
>> 
>>> Charles Hixson wrote:
>>>> Lars T. Kyllingstad wrote:
>>>>> Michal Minich wrote:
>>>>>> Hello rmcguire,
>>>>>>
>>>>>>> why is this not a compiler bug?
>>>>>>> because:
>>>>>>> import std.stdio;
>>>>>>> void main() {
>>>>>>> float f=0.01;
>>>>>>> writefln("%0.2f->%d",f,cast(int)(f*100f));
>>>>>>> writefln("%0.2f->%d",f,cast(int)(.01*100f));
>>>>>>> writefln("%0.2f->%f",f,(f*100f));
>>>>>>> }
>>>>>>> results in:
>>>>>>> 0.01->0
>>>>>>> 0.01->1
>>>>>>> 0.01->1.000000
>>>>>>> I would say something is dodgy.
>>>>>>>
>>>>>>> -Rory
>>>>>>>
>>>>>> I think this may be case of:
>>>>>> At comple time floating point computations may be done at a higher
>>>>>> precision than run time.
>>>>>
>>>>> Yes, if you do this:
>>>>>
>>>>> float f = 0.01;
>>>>> float g = f * 100f;
>>>>> real r = f * 100f;
>>>>> writeln("%s, %s, %s", f, cast(int) g, cast(int) r);
>>>>>
>>>>> you get:
>>>>>
>>>>> 0.01, 0, 1
>>>>>
>>>>> I believe just writing cast(int)(f*100f) is more or less the same as the
>>>>> 'real' case above.
>>>>>
>>>>> -Lars
>>>> Can that *really* be the explanation??  I know that float doesn't have all that much precision, but I thought it was more than 5 or 6 places...and this is, essentially, two places.
>>>
>>> Yes it does, but that's not what matters.
>>>
>>> Say that for floats, the representable number closest to 0.01 is 0.01000000000001. (This is just an example, I don't know the true number.) Then you have a lot more precision than the two digits you mention, and multiplying with 100 gives 1.000000000001. Round this towards zero (which is what cast(int) does) and you get 1. This is the 'float g = f*100f;' case in my example.
>>>
>>> Now, say that for reals, which (I think) is what the compiler uses
>>> internally, the number closest to 0.01 is
>>>      0.0099999999999999999999999999999999999999.
>>> Again, just an example, the point is that the precision is higher than
>>> the above, but the closest number is now smaller than 0.01. Multiply
>>> this by 100, and you get
>>>      0.99999999999999999999999999999999999999.
>>> This number will be cast to the integer 0, which happens in the OP's case.
>>>
>>> I admit I'm no expert in these things, but I suspect this is how it goes. By the way, I recommend Don's excellent article on floating-point numbers. It has really cleared things up for me:
>>>
>>>    http://www.digitalmars.com/d/2.0/d-floating-point.html
>>>
>>> -Lars
>>>
>> 
>> Hmm, thanks.
>> 
>> pity its not consistent. Would be nice if you didn't have to learn everything about floating point just to make a calculator.
> 
> 
> You don't, really.  You just stay away from casts. :)  IMHO, casts are only good for hammering a value of one type into a different type, no worries about the result.
> 
> I (almost) never use cast(int) to round floating-point numbers.  There are two reasons for this:
> 
>    1. It always rounds towards zero, which is usually not what I want.
>    2. It doesn't perform any checks, and can easily overflow.
> 
> If the rounding mode is not important, or if for some reason I want the same result as cast(int), I use std.conv.to().  This function checks that the converted number fits in the target type and then performs an ordinary cast.  (Note: D2 only!)
> 
>    import std.conv;
> 
>    void main()
>    {
>        int i = to!int(1.23);     // OK
>        assert (i == 1);
> 
>        int j = to!int(real.max); // ConvOverflowError
>    }
> 
> If "natural" rounding, i.e. towards nearest integer, is what I want, then I use std.math.lround() or std.math.round().  There are a lot of rounding functions in std.math that do different things and that are worth checking out.
> 
>    import std.math;
> 
>    void main()
>    {
>        long i = lround(1.23);
>        assert (i == 1);
> 
>        long j = lround(0.5);
>        assert (j == 1);
>    }
> 
> -Lars
> 


err, right.

good point, hehe.

November 05, 2009
On Wed, 04 Nov 2009 19:38:45 -0500, Joel Christensen <joelcnz@gmail.com> wrote:

>> To be safe, whenever converting to int, always add a small epsilon.  I think you can use float.epsilon, but I don't have any experience with whether that is always reasonable.
>>  -Steve
>
> Thanks every one for the replies.
>
> I still have problems. How do I use epsilon? 'real' helps in my example program but not in my money program. I think there's a function out there that does dollars and cents (eg. .89 -> 89c and 1.00 -> $1), but I'm interested how to solve this problem.

Might I suggest using integers instead of floating point.

My experience with floating point is limited, but I've been burned enough to avoid using it wherever possible.  To be fair, there are certainly many cases where floating point is useful, like in statistics, but when I can use integers, I do.

Translating money into integers is as easy as using the lowest denomination as 1.

For example, if you always store your money in cents, then $1 is simply stored as 100.  If you want to add tax (say 5%), you do x * 5 / 100.  If you want to do rounding, then it's a little trickier, but not too bad: (x * 5 + 50) / 100.  The trick is to add half the value of the denominator before dividing to do exact rounding.  I've written lots of code that uses decimal numbers without ever using floating point.

It's also fun to experiment with fraction types, which are more exact when you are dealing with rational numbers.  I once had a very comprehensive C++ fraction type that used a templated base type for the numerator/denominator.  I even ported it to Java using BigInt as the numerator/denominator types.  It could do very exact calculations with pretty useable speed.  This was only in algorithmic coding competitions, I've never had a real world use for them (though I know they exist).

There are types in other languages such as C#'s decimal that do a lot of the work for you.  It would be a nice addition to D.

Hope this helps.

-Steve
November 05, 2009
Joel Christensen wrote:
>> To be safe, whenever converting to int, always add a small epsilon.  I
>> think you can use float.epsilon, but I don't have any experience with
>> whether that is always reasonable.
>>
>> -Steve
>
> Thanks every one for the replies.
>
> I still have problems. How do I use epsilon? 'real' helps in my example
> program but not in my money program. I think there's a function out
> there that does dollars and cents (eg. .89 -> 89c and 1.00 -> $1), but
> I'm interested how to solve this problem.

If you're working with an accounting function, you should use bucket rounding.  I don't think there's any standard routine for it, but basically first you round, and then you accumulate the error created by rounding in a separate variable.  Then when you've processed the "column" or "batch" of numbers, you take the accumulated error and redistribute it over the numbers in a way that makes sense to you. (Perhaps proportional to the size of the number, or some such.)  The exact procedure for redistributing the error is case specific, which is probably why there aren't any standard functions to handle this, but uniform distribution rarely works, because the goal is that the totals precisely equal the value.  (Note that for this you don't want to use floating point values, but rather fixed point values...which can be emulated with integers that are converted to floats when it's time to export them.)  Remember, accounting routines aren't allowed to lose or generate any extra pennies.

If you want to say "That's a silly way to thing about numbers", I'll agree with you.  But the accountants who wrote the rules back in the 1800's (or earlier?) didn't use computers.  They used abacuses, and pen and ink.  And preventing cheating in various forms was one of their primary goals.

November 06, 2009
Seems random. I've got it working though, changed real to float. I seem to get different results with my little test program though.

Here's from my full program. (I think there's a function in phobos to do this any way):

char[] cashToString(dub money,char[] paddingForCents="".dup) {
  char[] amount;
  if ( money<1f ) {
    float cents=money*100f;
    amount=format("%s%d",paddingForCents,cast(int)cents);
  }
  else
    amount=format("%0.2f",money);
  return
    format("%s%s%s", money>=1f ? "$" : "", amount, money<1f ? "c" : "");
}
1 2
Next ›   Last »