views:

3077

answers:

10

I know, the question seems to be strange. Programmers sometimes think too much. Please read on...

In C I use signed and unsigned integers a lot. I like the fact that the compiler warns me if I do things like assigning a signed integer to an unsigned variable. I get warnings if I compare signed with unsigned integers and much much more.

I like these warning. They help me to keep my code clean.

Why doesn't we have the same luxury for floats? A square-root will definitely never return a negative number. There are other places as well where a negative float value has no meaning. Perfect candidate for an unsigned float.

Btw - I'm not really keen about the single extra bit of precision that I could get by removing the sign-flag from the floats. I'm super happy with the float as they are right now. I'd just like to mark a float as unsigned sometimes and get the same kind of warnings that I get with integers.

I'm not aware about any programming language that supports unsigned floating point numbers.

Any idea why they don't exist?


EDIT: I know that the x87 FPU has no instructions to deal with unsigned floats. Lets just use the signed float instructions. Miss-use (e.g. going below zero) could be considered undefined behaviour in the same way as overflow of signed integers is undefined.

+5  A: 

I guess it depends on that the IEEE floating points specifications only are signed and that most programming languages use them.

wikipedia articla on ieee floating point numbers

Edit: Also, as noted by others, most hardware does not support non-negative floats, so the normal kind of floats are more efficient to do since there is hardware support.

Tobias Wärre
+1  A: 

I suspect it is because the underlying processors targeted by C compilers don't have a good way of dealing with unsigned floating point numbers.

Brian Ensink
Did the underlying processors have a good way of dealing with signed floating-point numbers? C was getting popular when floating-point auxiliary processors were idiosyncratic and hardly universal.
David Thornley
I don't know all the historical timelines but there was emerging hardware support for signed floats, although rare as you point out. Language designers could incorporate support for it while compiler backends had varying levels of support depending on the targeted architecture.
Brian Ensink
+26  A: 

Why C++ doesn't have support for unsigned floats is because there is no equivalent machine code operations for the CPU to execute. So it would be very inefficient to support it.

If C++ did support it, then you would be sometimes using an unsigned float and not realizing that your performance has just been killed. If C++ supported it then every floating point operation would need to be checked to see if it is signed or not. And for programs that do millions of floating point operations, this is not acceptable.

So the question would be why don't hardware implementers support it. And I think the answer to that is that there was no unsigned float standard defined originally. Since languages like to be backwards compatible, even if it were added languages couldn't make use of it. To see the floating point spec you should look at the IEEE standard 754 Floating-Point.

You can get around not having an unsigned floating point type though by creating a unsigned float class that encapsulates a float or double and throws warnings if you try to pass in a negative number. This is less efficient, but probably if you aren't using them intensely you won't care about that slight performance loss.

I definitely see the usefulness of having an unsigned float. But C/C++ tends to chose efficiency that works best for everyone over safety.

Brian R. Bondy
C/C++ does not require specific machine code operations to implement the language. Early C/C++ compilers could generate floating point code for the 386 - a CPU with no FPU! The compiler would generate library calls to emulate FPU instructions. Therefore, a ufloat is could be done without CPU support
Skizz
Skizz, while that is correct, Brian already addressed this - that because there is no equivalent machine code the performance will be horrible by comparison.
Tony k
@Brian R. Bondy: I lost you here: "because there is no equivalent machine code operations for the CPU to execute...". Can you please explain, in simpler terms?
Lazer
+4  A: 

I believe the unsigned int was created because of the need for a larger value margin than the signed int could offer.

A float has a much larger margin, so there was never a 'physical' need for an unsigned float. And as you point out yourself in your question, the additional 1 bit precision is nothing to kill for.

Edit: After reading the answer by Brian R. Bondy, I have to modify my answer: He is definitely right that the underlying CPUs did not have unsigned float operations. However, I maintain my belief that this was a design decision based on the reasons I stated above ;-)

Treb
Also, addition and subtraction of integers is the same signed or unsigned -- floating point, not so much. Who would do the extra work to support both signed and unsigned floats given the relatively low marginal utility of such a feature?
ephemient
+7  A: 

(As an aside, Perl 6 lets you write

subset Nonnegative::Float of Float where { $_ >= 0 };

and then you can use Nonnegative::Float just like you would any other type.)

There's no hardware support for unsigned floating point operations, so C doesn't offer it. C is mostly designed to be "portable assembly", that is, as close to the metal as you can be without being tied down to a specific platform.

[edit]

C is like assembly: what you see is exactly what you get. An implicit "I'll check that this float is nonnegative for you" goes against its design philosophy. If you really want it, you can add assert(x >= 0) or similar, but you have to do that explicitly.

ephemient
Does this actually work yet in Rakudo?
Sol
http://svn.perl.org/parrot/trunk/languages/perl6/docs/STATUS says yes, but `of ...` doesn't parse.
ephemient
+2  A: 

I think Treb is on the right track. It's more important for integers that you have an unsigned corresponding type. Those are the ones that are used in bit-shifting and used in bit-maps. A sign bit just gets into the way. For example, right-shifting a negative value, the resulting value is implementation defined in C++. Doing that with an unsigned integer or overflowing such one has perfectly defined semantics because there is no such bit in the way.

So for integers at least, the need for a separate unsigned type is stronger than just giving warnings. All the above points do not need to be considered for floats. So, there is, i think, no real need for hardware support for them, and C will already don't support them at that point.

Johannes Schaub - litb
+2  A: 

A square-root will definately never return a negative number. There are other places as well where a negative float value has no meaning. Perfect candidate for an unsigned float.

C99 supports complex numbers, and a type generic form of sqrt, so sqrt( 1.0 * I) will be negative.


The commentors highlighted a slight gloss above, in that I was referring to the type-generic sqrt macro rather than the function, and it will return a scalar floating point value by truncation of the complex to its real component:

#include <complex.h>
#include <tgmath.h>

int main () 
{
    complex double a = 1.0 + 1.0 * I;

    double f = sqrt(a);

    return 0;
}

It also contains a brain-fart, as the real part of the sqrt of any complex number is positive or zero, and sqrt(1.0*I) is sqrt(0.5) + sqrt(0.5)*I not -1.0.

Pete Kirkham
Yes, but you call a function with a different name if you work with complex numbers. Also the return type is different. Good point though!
Nils Pipenbrinck
The result of sqrt(i) is a complex number. And since the complex numbers aren't ordered, you can't say a complex number is negativ (i.e. < 0)
quinmars
quinmars, sure it's not csqrt ? or do you talk about math instead of C? i agree anyway that it's a good point :)
Johannes Schaub - litb
Indeed, I was talking about math. I've never dealt with the complex numbers in c.
quinmars
Pete, great i didn't know C could deal with that stuff polymorphical like this. now i know what this "tgmath.h" is all about. thanks :)
Johannes Schaub - litb
wow! thanks for pointing that out!
Nils Pipenbrinck
A: 

In Euphoria, you can create User Defined Types to handle this sort of type checking for you. You can also turn the type checking on or off, so that you don't have to take the performance hit when ready to release.

type unsigned_float( object u )
    if atom(u) and u >= 0 then
        return 1
    end if
    return 0
end type

Since this is a relatively obscure language, an 'atom' is either a 31-bit signed integer, or a double, whereas an 'object' can be any data type.

Matt Lewis
+2  A: 

I think the main reason is that unsigned floats would have really limited uses compared to unsigned ints. I don't buy the argument that it's because the hardware doesn't support it. Older processors had no floating point capabilities at all, it was all emulated in software. If unsigned floats were useful they would have been implemented in software first and the hardware would have followed suit.

Ferruccio
The PDP-7, C's first platform, had an optional hardware floating point unit. The PDP-11, C's next platform, had a 32-bit floats in hardware. 80x86 came a generation later, with some technology that was a generation behind.
ephemient
+4  A: 

There is a significant difference between signed and unsigned integers in C/C++:

value >> shift

signed values leave the top bit unchanged (sign extend), unsigned values clear the top bit.

The reason there is no unsigned float is that you quickly run into all sorts of problems if there are no negative values. Consider this:

float a = 2.0f, b = 10.0f, c;
c = b - a;

What value does c have? -8. But what would that mean in a system without negative numbers. FLOAT_MAX - 8 perhaps? Actually, that doesn't work as FLOAT_MAX - 8 is FLOAT_MAX due to precision effects so things are even more screwy. What if it was part of a more complex expression:

float a = 2.0f, b = 10.0f, c = 20.0f, d = 3.14159f, e;
e = (b - a) / d + c;

This isn't a problem for integers due to the nature of the 2's complement system.

Also consider standard mathematical functions: sin, cos and tan would only work for half their input values, you couldn't find the log of values < 1, you couldn't solve quadratic equations: x = (-b +/- root (b.b - 4.a.c)) / 2.a, and so on. In fact, it probably wouldn't work for any complex function as these tend to be implemented as polynomial approximations which would use negative values somewhere.

So, unsigned floats are pretty useless.

But that doesn't mean to say that a class that range checks float values isn't useful, you may want to clamp values to a given range, for example RGB calculations.

Skizz

Skizz
@Skizz: if representation is a problem, you mean if someone can devise a method to store floats that is as efficient as `2's complement`, there will be no problem with having unsigned floats?
Lazer