1. Problem with floor() function

floor() seems not always to give the expected results, due to some internal rounding problems?

object x, y 
x = 10 * 2.35 + 0.5 
y = floor(x) 
puts(1, sprint(x) & " -- " & sprint(y)) 

In this example, x = 24, but floor(x) is 23.

With 2.55 instead of 2.35, both results correctly are 26.

This is annoying, as it makes rounding unreliable and, BTW, sprintf shows similar (though not identical) inconsistencies:

puts(1, sprintf("%1.1f", 2.35)) 
puts(1, sprintf("%1.1f", 2.55)) 

The first line prints 2.3, the second one 2.6

(Euphoria 3.1, Windows XP)

Is there a way to work around this problem, or will it be solved with version 4.0?

Robert Schächter

new topic     » topic index » view message » categorize

2. Re: Problem with floor() function

RobertS said...

floor() seems not always to give the expected results, due to some internal rounding problems?

object x, y 
x = 10 * 2.35 + 0.5 
y = floor(x) 
puts(1, sprint(x) & " -- " & sprint(y)) 

In this example, x = 24, but floor(x) is 23.

With 2.55 instead of 2.35, both results correctly are 26.

This is annoying, as it makes rounding unreliable and, BTW, sprintf shows similar (though not identical) inconsistencies:

puts(1, sprintf("%1.1f", 2.35)) 
puts(1, sprintf("%1.1f", 2.55)) 

The first line prints 2.3, the second one 2.6

(Euphoria 3.1, Windows XP)

Is there a way to work around this problem, or will it be solved with version 4.0?

Robert Schächter

The way is to avoid rounding, because it is done by hardware in a way that can be figured, but not easily at all. Instead, use integers, and divide by some factor when appropriate.

object x, y, z 
z = 100 
x = 10 * 235 + 50 -- z*original x 
y = floor(x/z)    -- x/z is original x 
puts(1, sprint(x/z) & " -- " & sprint(y)) 

will get you 24 both sides, because all intermediate calculations are made on intgers, thus guaranteed exact as long as magnitudes don't go above 9.0e15, more precisely power(2,53). This is a hardware quirk and is a standard.

CChris

new topic     » goto parent     » topic index » view message » categorize

3. Re: Problem with floor() function

RobertS said...

floor() seems not always to give the expected results, due to some internal rounding problems?

[snip]

Is there a way to work around this problem, or will it be solved with version 4.0?

This is an artifact of the way computers implement floating point numbers. The internal representation is in binary, meaning that some decimal representations of numbers can only be approximated (2.35 among them), and some approximations are better than others.

Take a look at this code to demonstrate what's going on:

object x, y  
x = 10 * 2.35 + 0.5  
y = floor(x)  
printf(1, "%0.16f %g %0.16f\n", {2.35,x,x,y}) 

The workaround for things like this is usually to use some epsilon to determine if the number is 'close enough' to whatever value you're interested in. It's hard to say what might be a good workaround without knowing the context. And due to the 'floating' in floating point, the appropriate epsilon might change given different magnitudes.

Matt

new topic     » goto parent     » topic index » view message » categorize

4. Re: Problem with floor() function

CChris said...

The way is to avoid rounding, because it is done by hardware in a way that can be figured, but not easily at all. Instead, use integers, and divide by some factor when appropriate.

object x, y, z 
z = 100 
x = 10 * 235 + 50 -- z*original x 
y = floor(x/z)    -- x/z is original x 
puts(1, sprint(x/z) & " -- " & sprint(y)) 

will get you 24 both sides, because all intermediate calculations are made on intgers, thus guaranteed exact as long as magnitudes don't go above 9.0e15, more precisely power(2,53). This is a hardware quirk and is a standard.

CChris

I tried this in C, and my results were 24. This suggests we may have a bug in the runtime itself, since our results are not identical to the C platform we build on top of.

Furthermore, I tried eu 2.3 and eu 4.0 alpha1 - 2.3 prints out 24 but 4.0 prints out 2.3

This might be acceptable (due to the way math is handled inside the interpreter or something, there may be some expected loss of precision) but we should at least figure out why this changed.

#include <stdio.h> 
#include <math.h> 
 
int main(int argc, char ** argv) 
{ 
	double x, y; 
	x = 10 * 2.35 + 0.5; 
	y = floor(x); 
	printf("%g\n", y); 
	return 0; 
} 
new topic     » goto parent     » topic index » view message » categorize

5. Re: Problem with floor() function

jimcbrown said...

I tried this in C, and my results were 24. This suggests we may have a bug in the runtime itself, since our results are not identical to the C platform we build on top of.

Furthermore, I tried eu 2.3 and eu 4.0 alpha1 - 2.3 prints out 24 but 4.0 prints out 2.3

This might be acceptable (due to the way math is handled inside the interpreter or something, there may be some expected loss of precision) but we should at least figure out why this changed.

It looks like C overestimates (I used Watcom), while euphoria underestimates. If you printf 2.35 with a %016f format, you'll get:

  c:  2.3500000000000001 
  eu: 2.3499999999999996 
The problem appears to be the way in which we build floating point representations. Basically, given a number like '2.35', euphoria does the following:

    x = 0 
    x += 2 
    x += 3/10 
    x += 5/100 

It's the x += 3/10 that gives us a problem. Interestingly, using scientific notation does not improve the situation, so I suppose I should take another look at the rounding going on there. Take a look at the following to see something interesting:

  printf(1, "%0.16f\n", 3/10 + 5/100) -- 0.3500000000000000 
  printf(1, "%0.16f\n", 2.0 + 0.3 )   -- 2.2999999999999998 
  printf(1, "%0.16f\n", 2.0 + 0.35 )  -- 2.3500000000000001 
  printf(1, "%0.16f\n", 0.35 + 2.0)   -- 2.3500000000000001 
  printf(1, "%0.16f\n", 35/100 + 2.0) -- 2.3500000000000001 

And just in case you're wondering, C gives the same results. So it looks like we should change the way we scan numbers. The regular case is relatively straightforwardrather than building the fraction part of the mantissa as a fraction, build it with integers and divide at the end. Though we may need to fall back to scientific notation if there are too many digits.

And the scientific parsing will take some additional study to see why it gives the results that it does.

Matt

new topic     » goto parent     » topic index » view message » categorize

6. Re: Problem with floor() function

mattlewis said...

Interestingly, using scientific notation does not improve the situation, so I suppose I should take another look at the rounding going on there.

I think I was wrong. Using scientific notation appears to be fine (in 4.0).

mattlewis said...

So it looks like we should change the way we scan numbers. The regular case is relatively straightforwardrather than building the fraction part of the mantissa as a fraction, build it with integers and divide at the end. Though we may need to fall back to scientific notation if there are too many digits.

r1295 gets this right now by only doing the division at the end.

Matt

new topic     » goto parent     » topic index » view message » categorize

7. Re: Problem with floor() function

Thank you for your comments and explanations!

Now, to me it seems that sprint does the rounding that turns the binary number back to the intended decimal value?

object x, x1 
x = 10 * 2.35 + 0.5 
x1 = value(sprint(x)) 
x = x1[2] 

Not very elegant, but this way, x is actually exactly 24.

I need this for a simple rounding function that rounds a floating point number x to n decimal places, consistently rounding up the digit 5. Without the part that deals with negative numbers it now looks like this:

function round(atom x, integer n) 
	object x1 
	x1 = value(sprint(x * power(10, n) + 0.5)) 
	x = x1[2] 
	x = floor(x) / power(10, n) 
	return x 
end function 

Hope this really works reliably...

Robert Schächter

new topic     » goto parent     » topic index » view message » categorize

Search



Quick Links

User menu

Not signed in.

Misc Menu