tags:

views:

408

answers:

3

Ok I have two functions the first of which looks like so:

let dlth x = float (x.ToString().Length)

which takes a float and returns the number of digits, that part works fine. The second function looks like this:

let droot x = ((x ** (1./(dlth x))) % 1.)

which takes a float and raises it to a power equal to 1.0/(number of digits), then takes the result and does modulus 1.0. Which should be zero for a whole number.

so for droot 36. it takes (36.0 ** (1.0/2.0)) which is 6.0 then 6.0 mod 1.0 equals 0.0;

Now, that works fine right up to where I try the number 81.0. (and all numbers higher than 81 that should work) which returns 1.0 for some reason, throwing off my pattern matching. Can anybody tell me why this is happening?

PostScript: this is part of a Project Euler solution. If you know which problem, please DO NOT post the Project Euler solution. I just need help figuring out why the modulus is returning funny results

+8  A: 

Floating-point arithmetic is fraught with peril in any language. On my box

printfn "%f" (0.9999999999999 % 1.0)

prints

1.000000

Hopefully that will help steer you in the right direction. If you really want to find out if a float "is an integer", then e.g. subtracting the nearest integer and seeing if the absolute value is less than some epsilon (e.g. 0.00001) is a decent bet.

Brian
I see what you mean, but I think with the range of numbers I'm checking it would be semi-impossible to create a test that works for everything. I think what I'm going to do is see if I can rework it to avoid floats entirely.
AvatarOfChronos
A: 

I agree with Brian that relying on exact floating point representations is dangerous, but I'm not able to reproduce your issue. For me, droot 81.0 gives the expected result of 0.0. What version of F# are you using? Are you running on .NET or Mono?

kvb
I've tried it on both. the only difference is where the problem starts. on .net it starts acting funny on 216. on mono it starts acting funny on 81. both mono and .net have the same problem they return a wrong modulus with this code. and I'm using F# 1.9.6.16
AvatarOfChronos
Yeah, the problem is that (216.0 ** (1.0 / 3.0)) gives 5.9999999999999991... The modulus operator then gives 0.9..., which is being displayed as 1.0. You can demonstrate to yourself that it's not actually 1.0 by doing a simple equality comparison. Unfortunately, you're going to need to come up with a more robust way to calculate what you want.
kvb
I don't see how the cube root of 216 is coming back as 5.9999999...1.Guess I'm going to have to dig into how .Net does floating point math and see what I can learn about it.
AvatarOfChronos
It's not "how .Net does floating-point math", it's "floating-point math". 1.0 / 3.0 cannot be represented exactly in a finite number of binary digits, the value will always be a little off.
Brian
+1  A: 

If you're getting float inaccuracies, you could take the performance hit with .NET's decimal data type (which is able to represent all numbers representable in base-10). Other than that use more precision (double) or integer math.

MighMoS