1

I'm trying to initialize a float to value infinity, and without using INFINITY macro.

float f[] = {
            0b01111111100000000000000000000000, // 0x7f800000
            0x7f800000 -1,
            0x7f800000 -2,
            0x7f800000 -64,
            0x7f800000 -65
        };

Printed:

2139095040.000000
2139095040.000000
2139095040.000000
2139095040.000000
2139094912.000000

Why isn't the first data infinity (like 1 / 0.0) and the others don't change until the last data?

0x7f7fffff should be the biggest value that a float can be and trying here 0x7f80000 is considered infinity.

untitled
  • 389
  • 4
  • 13
  • 4
    The values on the array will be *converted* to their floating point equivalents, the same way what happens when you do e.g. `float a = 5;` – Some programmer dude Jan 26 '16 at 19:23
  • You'd have to reinterpret the binary representation as float. I do not know a portable way for that, but in practice a union might work in most cases: `union { unsigned int i; float f; } test;` Set test.i and read test.f. Or just memcpy an int onto a float. – Ctx Jan 26 '16 at 19:26
  • 1
    The printed values other than the last are all the same because type `float` has less precision (but greater range) than does type `int`. The conversion to `float` loses precision, so that the first four `int` values all convert to the same `float` value, whereas the last `int` is enough different to convert to a different `float`. – John Bollinger Jan 26 '16 at 19:27
  • Then the "problem" is that data loses precision. @Ctx using union it gives inf. With union does the assignment never lose precision? – untitled Jan 26 '16 at 19:36
  • @SimoneBonato You mix up things here. Assume, you have a number, 0x7f800000. The binary representation of this number in integer format is: 7f 80 00 00. This number _also_ has a binary representation as a float, which is something completely different. And the conversion between these binary formats takes place, when you _cast_ the integer to float (which is done implicitly, when assigning it to a float variable). The union does not change the binary representation, but allows you, to interpret the same representation as float or as int. – Ctx Jan 26 '16 at 19:39
  • Use `1.0f/0.0f` instead. – chux - Reinstate Monica Jan 26 '16 at 19:48
  • @chux I tried that in MSVC as initialisation - it would not compile. – Weather Vane Jan 26 '16 at 19:49
  • @chux What is the difference from `1.0/0.0`? – untitled Jan 26 '16 at 19:50
  • @Simone Bonato `1.0f/0.0f` is `float`. `1.0/0.0` is `double`. Same "value". – chux - Reinstate Monica Jan 26 '16 at 19:51
  • BTW: Why not use `INFINITY`? – chux - Reinstate Monica Jan 26 '16 at 19:51
  • @chux that works, but with compiler warnings only. – Weather Vane Jan 26 '16 at 19:53
  • @Weather Vane what warning (double to float?). Then could use `((float) (FLT_MAX*2.0))`. – chux - Reinstate Monica Jan 26 '16 at 19:55
  • @chux *warning C4056: overflow in floating-point constant arithmetic*. The earlier error was *error C2124: divide or mod by zero* – Weather Vane Jan 26 '16 at 19:55
  • @Ctx Is `0b01111111100000000000000000000000` the binary representation as a float of infinity? – untitled Jan 26 '16 at 19:57
  • `0b01111111100000000000000000000000` is `2139095040` If that is assigned to a `float`, it will have the same value. – chux - Reinstate Monica Jan 26 '16 at 19:59
  • @SimoneBonato According to IEEE754 and 32-bit, yes – Ctx Jan 26 '16 at 19:59
  • @chux is that so? And what is 0b11111111100000000000000000000000 then? May be positive or negative, it's a matter of interpretation. Or even -inf when it's the binary representation of a float in IEEE754 – Ctx Jan 26 '16 at 20:01
  • The `float` value `+infinity` in binary is `01111111100000000000000000000000`, `-infinity` is `11111111100000000000000000000000`. The exponent `11111111` indicates infinity, plus the leading sign bit. – Weather Vane Jan 26 '16 at 20:06
  • @Ctx So the reason why if I assign `0b01111111100000000000000000000000` to a float, it isn't "inf" is that float has lost precision? – untitled Jan 26 '16 at 20:07
  • @Ctx OP is using a compiler which allows code with binary constants like `0b11111111100000000000000000000000`. This 32-bit value would have the value of 4286578688. – chux - Reinstate Monica Jan 26 '16 at 20:07
  • You can't just assign a binary value to a `float` like that. It's a **binary dump**. – Weather Vane Jan 26 '16 at 20:09
  • I haven't reached that warning (I use gcc), do you know any pages about binary dump? – untitled Jan 26 '16 at 20:19
  • I meant it is a bit by bit description of the `float` - not it's actual value. See [this](https://en.wikipedia.org/wiki/Single-precision_floating-point_format). – Weather Vane Jan 26 '16 at 20:23
  • So a part of `*(int*)&f` aren't others methods to assign to a float infinite value? (except using constants) – untitled Jan 26 '16 at 20:40
  • Why don't you want to use the `INFINITY` macro? That's what it's for. – Keith Thompson Jan 26 '16 at 20:49
  • Integer constants, whether hexadecimal like `0xff800000` or binary like `0b11111111100000000000000000000000` (when supported as a compiler-specific extension) do not indicate representations. They indicate *values*. `0xff800000` has the value 4286578688, and is of the first of types `int`, `unsigned int`, `long int`, or `unsigned long int`, that can hold its value. If you assign it to a floating-point type, the value is converted, yielding an approximation of `4286578688.0`. – Keith Thompson Jan 26 '16 at 20:53
  • @Keith Thompson Thanks for your explanation. I don't want to use that macro because I want to do experience directly with IEEE 754... – untitled Jan 26 '16 at 21:05
  • C doesn't guarantee that floating-point uses an IEEE 754 representation. You should update your question to make it clear that you're specifically talking about IEEE 754 infinity. You should also explain why you don't want to use the `INFINITY` macro; your reasons would probably also apply to the other obvious solutions, such as using `HUGE_VAL`. – Keith Thompson Jan 26 '16 at 21:26
  • In my question I had linked to a page about IEEE 754, however I thought C normally used IEEE 754. However: yes, I like alternative ways. – untitled Jan 26 '16 at 21:37

3 Answers3

2

The reason of your problem is notations:

  • 0x7f800000
  • 0b01111111100000000000000000000000

This notations related to int type and when you assigning int to float it means implicit conversion from int to float. In this case both of your numbers is 2139095040 in decimal and it will be conerted to float type.

To avoid this problem you may assign value in exact bit positions of 4 bytes. Here a couple of examples.

float f;
*(int*)&f = 0x7f800000;

Or you can use unions

union u_fi {
    float f;
    int   i;
} fi;
fi.i = 0x7f800000;

But be careful when use this 2 solutions. It won't work safely when int is more than 4 bytes in first case and won't work at all when int is big-endian. So this solutions are platform dependent and I recommend to use macro like it shown below.

Another solution to your problem is to use very large number wich converts to float as inf. For this you can use macro as in <math.h>

#define _HUGE_ENUF  1e+300
#define INFINITY   ((float)(_HUGE_ENUF * _HUGE_ENUF))
float f = INFINITY;
ComradeAndrew
  • 148
  • 3
  • 11
  • Beware of endianness, `float` does not necessarily have the same as `int`. And `int` may not be the same size as `float`. Suggest using `uint32_t`. – Weather Vane Jan 26 '16 at 20:24
  • @WeatherVane, yes. You are right. It should be something like `uint32_t`, but I prefer to use _native_ types which do not require additional headers. In MSVC it will be `__int32` Anyway if `int` will be more than 4 bytes it won't broke conversion and `float` will be `inf` – ComradeAndrew Jan 26 '16 at 20:29
  • 1
    `*(int*)&f` exactly what does it mean? – untitled Jan 26 '16 at 20:31
  • MSVC 15.0 defines `uint32_t` in `stdint.h` which is a standard type, but `__int32` is not. – Weather Vane Jan 26 '16 at 20:34
  • @SimoneBonato, this expresion consists of 3 parts: `&f` - operation takes address of `f` variable. Then `(int*)` _casts_ this address to **pointer to int**. And last one * is operator for pointers which gives to write value in address which pointer is containing. So we represent memory of our `float` variable as `int` and assign value like you assume it to assign in your question. Not by casting to int. – ComradeAndrew Jan 26 '16 at 20:41
  • @SimoneBonato `*(int*)&f` takes the address of `float f`, so it is a pointer to `float`, then casts that to a pointer to `int`, and writes the `int` value `0x7f800000` to that address. That is *not* the value of the infinite `float` but the *same bit pattern* that the `float` uses to represent infinity. It's a shortcut way of using the `union` method, but I believe it's frowned on to cast pointers from one type to another. – Weather Vane Jan 26 '16 at 20:44
  • @WeatherVane, I think additional headers isn't necessary. Especially given the fact that the size of `int` does not matter. – ComradeAndrew Jan 26 '16 at 20:45
  • 1
    It does matter when `int` is big-endian. – Weather Vane Jan 26 '16 at 20:46
  • 1
    @WeatherVane, if so this solution isn't cross-platform. For cross-platform solution author should use INFINITY macro from `` header. – ComradeAndrew Jan 26 '16 at 20:51
  • *"I'm trying to **initialize** a float to value infinity, and without using INFINITY macro."* – Weather Vane Jan 26 '16 at 20:53
  • 3
    `*(int*)&f` violates the strict aliasing rule and should not be used. – M.M Jan 26 '16 at 22:15
  • @M.M points out, you should use valid method for type-punning (e.g. `memcpy` or type unions) – Simon Byrne Jan 28 '16 at 09:50
0

You can use the macro HUGE_VAL. Or, if you prefer, you can initialize a dummy variable to zero, and divide by that variable (so you won't get a compilation error).

Mabus
  • 1,418
  • 12
  • 20
  • 2
    `HUGE_VALF` is of type `float`, `HUGE_VAL` of type `double`, and `HUGE_VALL` of type `long double`. – Keith Thompson Jan 26 '16 at 20:45
  • Well, I supposed that he couldn't use INFINITY because he is not working with C99 but maybe I was mistaken. Of course it would be easier to help if we knew why he can't use INFINITY. – Mabus Jan 26 '16 at 23:02
-1

Assuming you're using IEEE754 (which your question implies), you can give a result that would overflow. float can only represent values up to 3.4028235f38, so we could use

float x = 1e20f*1e20f;

Depending on your system/compiler you may need to specify flags (e.g. c99 or c11 will work) so that x is explicitly casted to float and not stored as higher intermediate precision.

Simon Byrne
  • 7,694
  • 1
  • 26
  • 50