2
votes

Rust is obviously a new language (0.8). It looks interesting, and I'm starting to look at it. I came across a mention of some software where float was changed to f64, so I wanted to find where the data-types, including float were defined. I couldn't find anything very specific. I did find:

There are three floating-point types: float, f32, and f64. Floating-point numbers are written 0.0, 1e6, or 2.1e-4. Like integers, floating-point literals are inferred to the correct type. Suffixes f, f32, and f64.

I guess "float" or "f" is 16 bit.

On a more general note (I'm no computer scientist), is it really worth messing around with all these small data-types like int, int32, int64, f, f32, f64 (to name a few). I can understand some languages having eg. a byte-type, because string is a fairly complex type. For numeric-types, I think it just creates unnecessary complexity. Why not just have i64 and f64 and call them int and float (or i64 and f64 to cater for future changes, or have float and int default to these).

Perhaps there are some low-level programs that require smaller values, but why not reserve the usage to those programs that need them and leave them out of the core? I find it an unnecessary chore to convert from eg. int to i64, etc., and what does it really achieve? Alternatively, leave them in the "core", but default to the 64-bit types. The 64-bit types are obviously necessary, whereas the rest are only necessary for specific cases (IMO).

2

2 Answers

8
votes

float, f32 and f64 are defined in the Rust manual: http://static.rust-lang.org/doc/0.8/rust.html#primitive-types

Specifically, for float:

The Rust type float is a machine-specific type equal to one of the supported Rust floating-point machine types (f32 or f64). It is the largest floating-point type that is directly supported by hardware on the target machine, or if the target machine has no floating-point hardware support, the largest floating-point type supported by the software floating-point library used to support the other floating-point machine types.

So float is not 16-bits, it's an alias for either f32 or f64, depending on the hardware.

To answer the second part of your question, in a low-language level like Rust, one can not simply postulate that a float is 64-bits, because if the hardware does not support this kind of floats natively then there is a significant performance penalty. One can neither have a single float type with an unspecified representation, because for a lot of use cases one need to have guarantees on the precision of the manipulated numbers.

In general, you would use float in the general case, and f32 or f64 when you have specific needs.

Edit: float has now been removed from the language, and there are only f32 and f64 float types now. The point being that all current architectures support 64-bits floats now, so float was always f64, and no use case were found for a machine-specific type.

6
votes

An answer to your "more general note": Rust exposes this variety of numeric types for two inter-related reasons:

  • Rust is a systems language.

    The compilation target (LLVM, or eventually concrete machine code, such as amd64) makes these distinctions to represent different hardware capabilities. Choosing different data types for different platforms impacts runtime performance, memory, and other resource usage. This flexibility is exposed to the programmer to allow them to fine tune their software to particular hardware.

  • Rust prioritizes interoperating with C.

    C may have made the same distinctions for the same justification, or it may have made the distinction because C is simpler when it provides fewer abstractions and delegates more to the underlying assembler.

    Either way, in order to interoperate between Rust and C without costly generic abstraction layers between them, the primitive types in each language correspond directly to each other.

Some advice:

If you don't care about performance, simply use the largest int, i64 or u64, or float type, f64. The resulting code will have easy-to-predict wrap-around and precisions behavior, but it will perform differently on different architectures, and it wastes space or time in some situations.

This contrasts with the conventional C wisdom (and maybe the Rust community would disagree with me), because if you use the "natural type for the architecture" the same code will perform well on multiple architectures. I'm of the opinion, however, that unexpected differences in wrap-around or float precision are a worse problem than performance. (My bias comes from dealing with security.)

If you want to avoid wrap around completely with integers, you can use bigints, which are costly software abstractions over hardware primitives.

As an aside, I appreciate explicitly seeing f64 as the type to remind me of precision errors. For example, JavaScript numbers are f64, but it may be easy to forget that and get surprised by JS code like var f = Math.pow(2, 53); f + 1 === f which evaluates to true. The same is true in Rust, but since I notice the type is f64 I'm more likely to remember.