9

I am porting program from C# to java. I've faced a fact that

Java

Math.pow(0.392156862745098,1./3.) = 0.7319587495200227

C#

Math.Pow( 0.392156862745098, 1.0 / 3.0) =0.73195874952002271

this last digit leads to sufficient differences in further calculations. Is there any way to emulate c#'s pow?

Thanx

Stepan Yakovenko
  • 8,670
  • 28
  • 113
  • 206
  • 5
    Your code won't even give reproducible results in .net, so forget about it. Related: http://stackoverflow.com/questions/6683059/are-floating-point-numbers-consistent-in-c-can-they-be – CodesInChaos Apr 12 '12 at 20:11
  • 12
    Really? The 17th significant digit is giving you "sufficient differences in further calculations"? Can you give us an example? – Bill the Lizard Apr 12 '12 at 20:12
  • 2
    If you compare it to calc.exe & wolfram alpha, the 1 is incorrect anyway. I would stick with Java's implementation. – Austin Salonen Apr 12 '12 at 20:14
  • 2
    What you're seeing is known as sensitivity to initial conditions and is an indicator that you are dealing with a chaotic system (http://en.wikipedia.org/wiki/Chaotic_dynamical_system#Sensitivity_to_initial_conditions). So if you can't embrace the chaos ... – andand Apr 12 '12 at 20:15
  • 2
    @andand: What he's seeing is differences in how Java and C# print numbers. There is no chaotic system at work. – Stephen Canon Apr 12 '12 at 22:12
  • Floating point results depend on compiler and cpu, java provides StrictMath.pow() and the strictfp keyword to ensure the same results across different jvms and cpus. Sadly this wont help much in this case since C# does not provide such functionality. – josefx Apr 12 '12 at 23:33
  • 1
    @Stephen Canon: I wasn't referring to the observed differences between Java and C# (though reading my comment I can see how that can be inferred). What I was referring to was the underlying problem the OP describes which results from this difference. If the differences from this state of affairs result in large and unacceptable differences later, he is dealing with a problem that is sensitive to initial conditions which indicates a possibly chaotic system. If the OP can't tolerate the chaotic nature of the system requiring this computation, (s)he's learning the wrong lesson. – andand Apr 13 '12 at 02:27

6 Answers6

35

Just to confirm what Chris Shain wrote, I get the same binary values:

// Java
public class Test
{
    public static void main(String[] args)
    {
        double input = 0.392156862745098;
        double pow = Math.pow(input, 1.0/3.0);            
        System.out.println(Double.doubleToLongBits(pow));
    }
}

// C#
using System;

public class Test
{
    static void Main()
    {
        double input = 0.392156862745098;
        double pow = Math.Pow(input, 1.0/3.0);            
        Console.WriteLine(BitConverter.DoubleToInt64Bits(pow));
    }
}

Output of both: 4604768117848454313

In other words, the double values are exactly the same bit pattern, and any differences you're seeing (assuming you'd get the same results) are due to formatting rather than a difference in value. By the way, the exact value of that double is

0.73195874952002271118800535987247712910175323486328125

Now it's worth noting that distinctly weird things can happen in floating point arithmetic, particularly when optimizations allow 80-bit arithmetic in some situations but not others, etc.

As Henk says, if a difference in the last bit or two causes you problems, then your design is broken.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 1
    *"distinctly weird things can happen in floating point arithmetic, particularly when optimizations allow 80-bit arithmetic"* - For more info, see my [question on C# floating-point consistency](http://stackoverflow.com/questions/6683059/are-floating-point-numbers-consistent-in-c-can-they-be) – BlueRaja - Danny Pflughoeft Apr 12 '12 at 20:55
  • No, Math.pow is different in C# and java. Please consider Double.doubleToLongBits(Math.pow(0.39215686274509803,1.0/3.0)) and BitConverter.DoubleToInt64Bits(Math.Pow(0.39215686274509803,1.0/3.0)). – Stepan Yakovenko Apr 13 '12 at 08:28
  • @user643540: Yes, if you specify the literal value directly in the call, you can end up with a different answer exactly as per my penultimate paragraph. But as everyone else has been saying - if one ULP being different is causing you problems, you should be concerned about your design. – Jon Skeet Apr 13 '12 at 09:20
18

If your calculations are sensitive to this kind of difference then you will need other measures (a redesign).

H H
  • 263,252
  • 30
  • 330
  • 514
  • 3
    It isn't a direct answer, no. But that's the issue with [XY problems](http://meta.stackexchange.com/questions/66377). Do you expect a piece of Java code "to emulate c#'s pow" ? – H H Apr 12 '12 at 20:22
13

this last digit leads to sufficient differences in further calculations

That's impossible, because they're the same number. A double doesn't have enough precision to distinguish between 0.7319587495200227 and 0.73195874952002271; they're both represented as

0.73195874952002271118800535987247712910175323486328125.

The difference is the rounding: Java is using 16 significant digits and C# is using 17. But that's just a display issue.

Lion
  • 18,729
  • 22
  • 80
  • 110
dan04
  • 87,747
  • 23
  • 163
  • 198
8

Both Java and C# return a IEEE floating point number (specifically, a double) from Math.Pow. The difference that you are seeing is almost certainly due to the formatting when you display the number as decimal. The underlying (binary) value is probably the same, and your math troubles lie elsewhere.

Chris Shain
  • 50,833
  • 6
  • 93
  • 125
  • 1
    It's easily possible for the last digits to actually differ. .net does not guarantee specific results, and it makes use of that liberty. – CodesInChaos Apr 12 '12 at 20:13
  • 1
    They both return a double *typed* value -- however, unless there is a FPU "POW" that is used (e.g. it is a function of the FPU, not any "strict" settings in the languages) then the algorithm used could be subtly different... would be interesting to have a small snippet to show this either way here, though. –  Apr 12 '12 at 20:14
1

Floating-point arithmetic is inherently imprecise. You are claiming that the C# answer is "better" but neither of them are that accurate. For example, Wolfram Alpha (which is much more accurate indeed) gives these values:

http://www.wolframalpha.com/input/?i=Pow%280.392156862745098%2C+1.0+%2F+3.0%29

If a unit's difference in the 17th digit is causing later computations to go awry, then I think there's a problem with your math, not with Java's implementation of pow. You need to think about how to restructure your computations so that they don't rely on such minor differences.

Ethan Brown
  • 26,892
  • 4
  • 80
  • 92
  • There are situations where you don't care about the last digits of the actual results, as long as they're consistent and reproducible. But in that case you can't use .net's built in floatingpoint types. – CodesInChaos Apr 12 '12 at 20:18
  • 1
    I've been corrected on this- floating point math is very precise. The issue is that it operates on **binary** numbers, so there may be no precise, terminating conversion from decimal to binary (similarly, there is no precise terminating representation of 1/3 in decimal, but in base-3 it would be expressed as .1). If your inputs cannot be accurately expressed in binary they will be rounded, and if you format your output as decimal it may be rounded again. – Chris Shain Apr 12 '12 at 20:20
  • "Very precise" is a subjective term. It's probably more precise than you will ever need unless you are working with highly chaotic systems. My point is that, with a limited number of storage space (64 bits), there is an upper limit to the precision with which you can express numbers, regardless of the base. – Ethan Brown Apr 12 '12 at 20:22
1

Seventeen digits' precision is the best any IEEE floating point number can do, regardless of language:

http://en.wikipedia.org/wiki/Double-precision_floating-point_format

duffymo
  • 305,152
  • 44
  • 369
  • 561