-8

Given the following basic code:

double x = 3;
return x *-1;

I expected the return to give -3 as the result. However, the actual value I get is about 1.844674e+19 (limited by output) or a crazy large number. Doing simply return -x; also does not work.

Changing the code as such:

double x = 3;
return x *-1.0;

Returns the correct output. This seems to be some integer vs double or Boolean issue. I understand that dividing integers without explicitly making it a double can give 0 instead of a fraction, but I have never seen it cause issues with multiplication or addition.

Can somebody tell me what happened or point to explanation of the behavior and how to deal with it?

Edit: Here is a verifiable example on my machine.

Function:

// [[Rcpp::export]]
double test1(double data)
{
  std::vector<double> testArray(data);
  return (testArray.size()*-1);
}

from R run with:

test1(4)

Result:

[1] 1.844674e+19

I was able to narrow down the issue to the size of of a vector. so for any positive integer size the .size() function multiplied by -1 results in the crazy value. Multiplying by -1.0 works perfectly fine.

Edit 2: I think I got it. Toby is right and it is an unsigned value that is not converted to double. In my original sample:

// [[Rcpp::export]]
double test1(double data)
{
  std::vector<double> testArray(data);
  double arraySize = testArray.size();
  return (arraySize*-1);
}

Works perfectly fine by first converting it to a double and multiplying by -1.0 does as well.

glennsl
  • 28,186
  • 12
  • 57
  • 75
MichaelE
  • 763
  • 8
  • 22
  • 2
    What is the signature of the function? – Paul Belanger Aug 13 '18 at 20:42
  • 5
    Can't reproduce: http://coliru.stacked-crooked.com/a/eb8079eb2e85b812 – SergeyA Aug 13 '18 at 20:43
  • Possible duplicate of https://stackoverflow.com/questions/588004/is-floating-point-math-broken#588014 – Jesper Juhl Aug 13 '18 at 20:45
  • This rcpp with R, but other than that is just like I posted above. The only thing that comes to mind is that -1 is somehow a Boolean which is optimized for all 1 with gives this crazy huge number. Somthing like why true in VBA is -1??? I really don't know. – MichaelE Aug 13 '18 at 20:47
  • @JesperJuhl `1.844674e+19` is more than 1/2 ulp away from `-3`. – Sneftel Aug 13 '18 at 20:48
  • I dont know rccp with R, but I doubt that there functions need no signature... what is the return type? – 463035818_is_not_an_ai Aug 13 '18 at 20:50
  • 1
    Almost sounds like UB is UB. – Michael Dorgan Aug 13 '18 at 20:52
  • Or that your compiler is really really bad. – Michael Dorgan Aug 13 '18 at 20:55
  • This is with Rcpp so the return type is NumericMatrix and the input is NumericMatrix. I take in data and do some math on it, much faster than even vectorized R. In debugging I found a data point (3) and I need to invert it as in long 3 so sell 3 to get flat hence -1. Should be simple but I get the crazy result. – MichaelE Aug 13 '18 at 20:58
  • I did try creating a clean basic function with the same data and just *-1 and it works perfectly fine. So now I am more lost than ever. – MichaelE Aug 13 '18 at 21:00
  • I have had R do some crazy things and just not work without a system restart, but never like this. Typically it would just not compile for no reason. I will try more tests and maybe restart and see if that helps. – MichaelE Aug 13 '18 at 21:02
  • 5
    @MichaelE Nobody can answer this question unless we see the rest of the code. The code is very important. Trying to describe it in English is absolutely no help. Either show us the code or its going to get deleted. – Martin York Aug 13 '18 at 21:18
  • 1
    @MichaelIE: Your mistake, you should have use type `double`, ie a minimal `Rcpp::cppFunction("double foo(double x) { return x * -1; }") ` works just fine. But by forcing `NumericMatrix` with a single argument you force its constructor which is NOT what you want here. – Dirk Eddelbuettel Aug 14 '18 at 00:44
  • 2
    You're not multiplying a double by `-1`; you're multiplying a **`std::size_t`** by -1 - that gives you a large positive value. Then (as you return it), it's converted to `double`. – Toby Speight Aug 14 '18 at 14:05
  • Thanks, you are right. It is an unsigned variable. If I set it to a double to the size it is converted to double, but if I set a NeumericMatrix or vector (which is internally double) the conversion does not happen. – MichaelE Aug 14 '18 at 14:23
  • Nevermind, it works perfectly fine with Numeric types if I properly set them to the positive unsigned value. Then negating works as espected. Thanks. – MichaelE Aug 14 '18 at 14:39

1 Answers1

1

Although you believe your code is similar to this:

#include <iostream>

double f()
{
    double x = 3;
    return x * -1;
}

int main()
{
    std::cout << f() << std::endl;
}

The code you have actually takes the type of x from the result of a vector's size() - this returns a std::size_t:

#include <cstdint>
#include <iostream>

double f()
{
    std::size_t x = 3;
    return x * -1;
}

int main()
{
    std::cout << f() << std::endl;
}

What happens here is that a std::size_t is multiplied by int. The rules of promotion when one argument is signed and one unsigned say that (source: CppReference)

if the unsigned operand's conversion rank is greater or equal to the conversion rank of the signed operand, the signed operand is converted to the unsigned operand's type.

std::size_t is usually higher rank than int, so we are now multiplying 3 by (std::size_t)-1 - a large number. Then when we return, this large number is converted to double to match the signature of f().

The issue can be avoided a few ways:

  • Store the result of size() into a variable of signed or floating-point type, and use that to multiply.
  • Convert the result of size() using static_cast<> to a signed or floating-point type when multiplying.
  • Multiply by a floating-point constant (e.g. -1.0) - this will cause the other argument to be promoted to floating-point.
Toby Speight
  • 27,591
  • 48
  • 66
  • 103