1

Edited for test case and compiler inconsistency. Recently I made a change to use the more modern < cmath > instead of < math.h >. While this compiled just fine as expected with no warnings, this one-line change for one file caused my test cases to fail. From my understanding, cmath just puts most of the functions/variables in the std namespace, and I don't make any changes to that namespace.

I'll note I am using

  • sqrt

  • abs (since I am using doubles here I would assume the compiler would automatically use the cmath/math.h form, not stdlib)

  • tan

out of any files potentially affected by this one-line change on one file. I do have a different file that is already using cmath, but does not affect the testing.

A test case with

#include <iostream>
#include <cmath>
int main() {

    double x = -0.546;
    double y = abs( x / sqrt(x*x + 1.0));
    double z = std::abs( x / sqrt(x*x + 1.0));

    std::cout << "Variable value is " << x << std::endl;
    std::cout << "Value of function using cmath " << z << std::endl;
    std::cout << "Value of function using stdlib " << y << std::endl;
    return 0;
}

gives output

Value is -0.546
Value of function using cmath 0.479221
Value of function using stdlib 0

on compiler (GCC) 8.3.1 20191121. Curiously, this is not the output on C++ shell, instead keeping the value of the function non-zero.

Not a real meerkat
  • 5,604
  • 1
  • 24
  • 55
Brinck
  • 69
  • 4
  • You seem to mention cmake in a few places where cmath makes more sense. Might be worth cleaning up the question. – paxdiablo Mar 28 '23 at 00:32
  • 1
    And editing in a [mre] as well. – sweenish Mar 28 '23 at 00:33
  • 1
    `cmath` also adds in some fun lil' templates. I've seen some interesting behaviours with `std::pow` templated wrapping round `::pow` behaving differently from `::pow` for reasons I was never able to understand. You may have encountered something similar, but the proof of the program is in the coding, to deliberately misquote a favored line of my grandmother's. – user4581301 Mar 28 '23 at 00:41
  • If you have `using namespace std` it can pick different overloads, e.g. cmath defines [tan(double) and tan(float)](https://en.cppreference.com/w/cpp/numeric/math/tan), but there is no function overloads in C language at all and it defines separate `tan(double)` and `tanf(float)` functions. – dewaffled Mar 28 '23 at 00:46
  • @paxdiablo, fixed to use cmath – Brinck Mar 28 '23 at 00:49
  • @sweenish, this is really more of question requiring a method or heuristic answer, mainly because I have a large data set going through the equations, and I'm not sure if some combination of variables may be an issue related to cmath vs math.h. I can give you a huge list with the equations in question, but overall the example may be difficult to reproduce. – Brinck Mar 28 '23 at 00:49
  • @dewaffled I am not using `using namespace std` . But I'm guessing that would be helpful here. – Brinck Mar 28 '23 at 00:51
  • If debugging was easy programmers would be a dime-a-dozen. Do what you can to isolate the sucker and you'll have a good Stack Overflow question. Leave it like this and there isn't much we can do but pitch guesses at you, and that's something Stackoverflow's not that good at. – user4581301 Mar 28 '23 at 00:57
  • 3
    I didn't request your code. I requested a [mre]. Being able to reproduce the error in a smaller program is overall beneficial. – sweenish Mar 28 '23 at 01:03
  • 2
    Can you extract 1 failing test case as a [mcve] that we can look at on [Compiler Explorer](https://godbolt.org/). Please read [mcve]. – Richard Critten Mar 28 '23 at 01:04

1 Answers1

5

The C++ standard library has defined overloads for std::abs that accept floating point types. These are declared in the std namespace only, as to not pollute the global namespace. More info on cppreference.

The same is not true for abs in the global namespace. Its origin is on stdlib.h. Since stdlib.h is a C library, and C does not support function overloads, the versions for floating point are defined instead as fabs and fabsf for double and float, respectively.

What you are actually doing when calling abs (in the global namespace) with a floating point value is casting that value to an integer. Since the value is guaranteed to be smaller than 1 in your example, that means that you are essentially always passing 0 as the argument.

If you turn on warnings (specifically -Wconversion), the compiler will warn you about this:

<source>: In function 'int main()':
<source>:6:23: warning: conversion from 'double' to 'int' may change value [-Wfloat-conversion]
     double y = abs( x / sqrt(x*x + 1.0));
                     ~~^~~~~~~~~~~~~~~~~

Curiously, this is a case where declaring using namespace std; would "solve the problem". Since std::abs(double) is a better overload, it would be chosen by overload resolution if the namespace std is imported into the global namespace. Not that I'm advocating doing this - it will have several other side effects that you may not be aware about. So the best thing to do is to either use std::abs explicitly, or only importing it specifically, and in a narrower scope (e.g.: placing using std::abs; in your function's scope).

Not a real meerkat
  • 5,604
  • 1
  • 24
  • 55