0

I'm implementing the printf() function. Nearly everything works correctly, but I have an issue when called as ft_printf("%f", -0.0). My program outputs 0.0 instead of -0.0. I can't use any libraries.

My condition for checking for negativity is simply "x<0" which doesn't include my problem. I've found a promising solution in Distinguish zero and negative zero.

this is what seems would solve my problem:

double a = -0.0;
printf("%d\n", (*((long *)&a) == 0x8000000000000000));

I'd like for this program to print 1 but in my program when I do it in my code it outputs 0. The part I have a problem understanding is how this: *((long *)&a) makes a number comparable to its hexadecimal counterpart.

S.S. Anne
  • 15,171
  • 8
  • 38
  • 76
spozzi
  • 65
  • 5
  • 2
    You are violating the strict aliasing rule, thus invoking undefined behavior. – machine_1 Aug 08 '19 at 16:59
  • 1
    If you want to do things the way you're doing them, you have to alias it with an array of characters. – Thomas Jager Aug 08 '19 at 17:01
  • Does "I can't use any libraries" include the the C standard library, as opposed to third-party libraries? – John Bollinger Aug 08 '19 at 17:15
  • 1
    Is there a good reason that you can't use libraries? You're going to have trouble finding a truly portable solution. – Thomas Jager Aug 08 '19 at 17:16
  • 1
    Doing 'Ecole 42' and it's against the rules to use anything that wasn't coded by me (with some exceptions) – spozzi Aug 08 '19 at 17:24
  • 1
    @spozzi So you're not allowed to use any built-in headers? – Thomas Jager Aug 08 '19 at 17:45
  • @spozzi If so, then you should state your limitations more clearly in your questions. If you really can't use standard headers (which are an intrinsic part of the language), then the code *must* be non-portable if you want to do so much as do any I/O. – Thomas Jager Aug 08 '19 at 17:52
  • 2
    The rule not to “use anything that wasn't coded by me” is very poorly stated. (And you ought to tell us the exact wording of the rule and its exceptions, so we can understand the rule.) `int main(void)` and `int main(int argc, char *argv[])` are given in the C standard, so you did not code those, so you cannot define `main`? Using the `signbit` macro of `math.h` is absolutely the correct way to test the sign of a floating-point number, and it ought to count as a basic language feature, not something off limits as using somebody else’s code. – Eric Postpischil Aug 08 '19 at 17:57
  • 2
    If you cannot use `signbit`, there is no fully portable correct solution. You could compare the bytes that represent some object `x` to the bytes that represent `-0.` and `0.`, and that would work in most implementations (and be strictly conforming to the C standard, if implemented correctly), but an implementation could have multiple representations of −0 and +0, and strictly conforming C code provides no way to know about and distinguish them. – Eric Postpischil Aug 08 '19 at 18:00
  • *I can't use any libraries.* Better not use `printf()` then... – Andrew Henle Aug 08 '19 at 18:24
  • Related: [What operations and functions on +0.0 and -0.0 give different arithmetic results?](https://stackoverflow.com/q/25332133/2410359) – chux - Reinstate Monica Aug 08 '19 at 18:34
  • @AndrewHenle: I expect they are not using `printf`; the first sentence of the question says they are implementing it. – Eric Postpischil Aug 08 '19 at 18:41

2 Answers2

7

The C math library (part of the standard library) provides a signbit macro that is absolutely the simplest and most portable way to check the sign of a floating-point number. If your requirement to avoid libraries means third-party libraries then it will serve your purpose.

Example:

#include <math.h>
#include <stdio.h>

void putsign(double d) {
    if (signbit(d)) {
        putchar('-');
    } else {
        putchar('+');
    }
}
John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • *"I can't use any libraries."* – S.S. Anne Aug 08 '19 at 17:14
  • 2
    `signbit` is a *macro*, so shouldn't require any libraries to be linked. [Demo](https://godbolt.org/z/h5Ri4B) – Toby Speight Aug 08 '19 at 17:52
  • 3
    @jl2210: On the other hand, `fputc` and friends definitely require `stdio.h`. And it's pretty well impossible to implement a varargs function such as `printf` without making use of `stdarg.h`. So at least part of the standard library is present. OP says there are "certain exceptions"; it would be useful to know what they are. – rici Aug 08 '19 at 18:09
  • Judging by the prefix `ft_` on the `printf` function the OP is implementing, I'd wager that they don't have the `signbit` macro available. Search for libft or lib42 for more details. – S.S. Anne Aug 08 '19 at 18:23
3

This is similar to a Codewars Kata that I completed. The trick is to divide by the number that you want to check is negative zero, like this:

int isPositiveZero(double a) {
    return 1/a == 1/0.0;
}

The division gives -infinity if a is negative zero, and infinity if a is zero.

If you want to do it your way, try this:

(*((long *)&a) & 0x8000000000000000)

Note that this violates the strict aliasing rule, which causes undefined behavior. This means that your program could do anything from printing the right result to making your computer sprout wings and fly away.

If you have math.h available and don't have an aversion to using macros from the standard C library, then this solution (using signbit) will also work (and be much more portable):

#include <math.h>

int isPositiveZero(double a) {
    return a == 0.0 && !signbit(a);
}
S.S. Anne
  • 15,171
  • 8
  • 38
  • 76
  • Does C guarantee that all possible `double` representations have positive and negative infinity values? – Thomas Jager Aug 08 '19 at 17:13
  • @ThomasJager I think so. It may be IEEE, though. – S.S. Anne Aug 08 '19 at 17:13
  • @ThomasJager See https://www.gnu.org/software/libc/manual/html_node/Infinity-and-NaN.html – S.S. Anne Aug 08 '19 at 17:16
  • *"IEEE 754 floating point numbers can represent positive or negative infinity"*. – S.S. Anne Aug 08 '19 at 17:18
  • 3
    I don't disagree that IEEE 754 floating-point types can handle it. My point is that this isn't a portable solution if you don't check that IEEE 754 floats are in use. As far as I know, C doesn't guarantee any given implementation. – Thomas Jager Aug 08 '19 at 17:18
  • 3
    In fact, no, C *does not* guarantee that floating-point types have representations of any infinity, positive or negative. They typically do, because IEEE 754 representations and arithmetic are fairly ubiquitous, but C does not require these. – John Bollinger Aug 08 '19 at 17:21
  • 1
    This is certainly more portable than `(*((long *)&a) == 0x8000000000000000)`, though. – S.S. Anne Aug 08 '19 at 17:21