Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

I'm toying around with LLVM and looking at what it would take to make yet another strongly-typed language, and now that I'm around the syntax, I've noticed that it seems to be a pet peeve of strongly typed language to warn people that their real constants won't fit inside a float:

// both in Java and C#
float foo = 3.2;
// error: implicitly truncating a double into a float
// or something along these lines

Why doesn't this work in Java and C#? I know it's easy to add the f after the 3.2, but is it really doing anything useful? Must I really be that aware that I'm using single-precision reals instead of double-precision reals? Maybe I'm just missing something (which, basically, is why I'm asking).

Note that float foo = [const] is not the same thing as float foo = [double variable], where requiring the cast seems normal to me.

share|improve this question
add comment

5 Answers

If you're designing a statically-typed language, you may want to look at how Haskell handles numeric literals. Here's an example of how they are typed in Haskell:

Prelude> :type 2
2 :: (Num t) => t
Prelude> :type 2.0
2.0 :: (Fractional t) => t
Prelude> :type pi
pi :: (Floating a) => a
What this means is that the literal 2 is of a type that is a member of the class Num, the literal 2.0 is of a type that is a member of the class Fractional, and pi returns a value of a type that is a member of the class Floating. The different types of numbers, for example Int and Float are members of the appropriate type classes. This allows the compiler to infer the type of the literal based on where it will be used or bound. If you pass 2 as a parameter to function that expects a Float, the compiler will call Float's implementation of fromInteger behind the scenes, which will convert it for you. This works because Float is a member of the type class Num, and a requirement for becoming a member of Num is having an implementation of fromInteger.

You'll need type inference to make all of this work. More about types and classes in Haskell can be found here.

share|improve this answer
    
If I understood correctly, you mean that it would be a good idea to infer the numeric type from the required type? –  zneak Feb 6 '11 at 22:31
    
@zneak: Yes. Type inference is a good option for statically-typed languages. Even if you want to declare types, with type inference the compiler can spot incorrect declarations. –  Larry Coleman Feb 7 '11 at 3:44
add comment

Java compiler is complaining rightfully because 3.2 (double) and 3.2f (float) are different numbers from its point of view (and from language specification's POV).

In binary form (IEEE 754, skipping sign and exponent field) numbers are clearly different, and it makes sense that compiler reports an error about truncation, loss of precision and probable rounding error.

3.2 : 1.1001100110011001100110011001100110011001100110011010
3.2f: 1.10011001100110011001101

It's the same type of error if you try to write:

short s = 70000;     // Assuming short is 16 bit integer
share|improve this answer
    
Is it really the same type of error? Both with a float and a double you end up with an inexact representation of 3.2. In your integer case, upgrading short to int, you get an exact representation. –  zneak Feb 6 '11 at 22:20
add comment

double is supposed to the “normal” floating-point type, and float a special-purpose type to use when you need to save memory. There's not a lot you can do with float, what with java.lang.Math or System.Math having a lot of double-only functions.

Note that float foo = [const] is not the same thing as float foo = [double variable], where requiring the cast seems normal to me.

They are the same: float foo = [expression of type double]. It keeps the compiler simpler to not artificially distinguish them.

share|improve this answer
    
I don't think they are the same. Real constants being typed to double is absolutely artificial, but the typing of a variable is not. If you think you need 64 bits, it's only normal that you have to cast to truncate. If the language decides you need 64 bits, it's another thing. –  zneak Feb 6 '11 at 22:26
    
(Also, 16-bits half-precision reals exist in hardware graphics, so renaming float to half would somehow imply renaming half to quarter. Might be confusing.) –  zneak Feb 6 '11 at 22:41
    
Yes, that is what I'm suggesting: 16-bit "quarter precision", 32-bit "half-precision", 64-bit "single-precision", and 128-bit "double precision"; all based around 64-bit being the default. –  dan04 Feb 6 '11 at 23:00
add comment

One should not warn about truncating floating-point quantities, constant or otherwise, to a 32 float. Any floating-point quantity has a single correct 32-bit representation. That representation will not uniquely identify the number, but will--if the original quantity is known precisely--identify a range of values the number is known to be within. In only one very narrow corner case will a conversion from double to float not yield a result strictly with the +/- 0.5lsb, and in that narrow case, the result will still be within 0.50000001lsb) What compilers should warn about (but alas do not) is casting in the other direction--feeding 32-bit floats into calculations which expect double. Casting from a double to a float should be considered perfectly fine if the result of the computation will never be converted back into a double. Conversely, casting from float to double should be considered dubious even when there would be no other basis for a diagnostic. For example, in the statement double foo = (1.0f / 10.0f); all of the floating-point constants are represented precisely, but the result is off by an amount billions of times larger than what should be expected in a double. Such an assignment is far more likely to represent a mistake than is e.g. the passing of a double to a drawing routine that expects a float; unfortunately, languages seem to regard the latter as worthy of a diagnostic and the former not.

share|improve this answer
add comment

Seems to me that a pedantic compiler complaining that something may lead to unexpected results is only ever a good thing.

The compiler could silently promote a literal to a suitable type but this involves guesswork. Did the author really want it promoted, or is what's there actually an error. Taking the guesswork out and just barfing has the advantage that the user is explicit in what the feed to the compiler, and should therefore know what they get.

share|improve this answer
1  
I don't see what kind of guesswork is involved. If I type in float x = 3.2; what makes it worth assuming that I made a mistake? –  zneak Feb 7 '11 at 14:10
2  
What type should "3.2" have? You have a choice of several. It gets even more complex in expressions. Suppose for example I have float x; and I want to use it in an expression, thus: x = ((95.5 * sin(y) + myfunc(z)) + 4.0); and I want that to all be calculated in double precision, happily tolerating a truncation to float when its done. How is the compiler to guess what to apply where? And if it did guess and got it wrong you'd be pretty annoyed. In GENERAL, these questions ("why can't compiler magic A to B for me") fail once you get away from simple constant assignments. –  quickly_now Feb 7 '11 at 22:22
    
@quickly_now The phrase "Note that float foo = [const] is not the same thing as float foo = [double variable], where requiring the cast seems normal to me." seems to be generalizable to what you're discussing here. The question is about constants/numeric literals, not variable expressions on the right-hand side. –  pwny Jul 12 '12 at 19:54
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.