Why is my arithmetic with a long long int behaving this way?Why is “using namespace std” considered bad practice?Why isnt int pow(int base, int exponent) in the standard C++ libraries?Why use static_cast<int>(x) instead of (int)x?Why can templates only be implemented in the header file?What does the C++ standard state the size of int, long type to be?What is the difference between const int*, const int * const, and int const *?Why is “using namespace std” considered bad practice?Easiest way to convert int to string in C++Why are elementwise additions much faster in separate loops than in a combined loop?Why is reading lines from stdin much slower in C++ than Python?Why is it faster to process a sorted array than an unsorted array?Why should I use a pointer rather than the object itself?
Is there any optimization for thread safety in for loop of Java?
Why did Gendry call himself Gendry Rivers?
Musical Shape on music stand
How does jetBlue determine its boarding order?
Test whether a string is in a list with variable
Average of samples in a period of time
Picking a theme as a discovery writer
Translation of "invincible independence"
How could a humanoid creature completely form within the span of 24 hours?
When does WordPress.org notify sites of new version?
Which "exotic salt" can lower water's freezing point by 70 °C?
Crime rates in a post-scarcity economy
Why were the rules for Proliferate changed?
Is throwing dice a stochastic or a deterministic process?
How can I test a shell script in a "safe environment" to avoid harm to my computer?
Employee is self-centered and affects the team negatively
Bash prompt takes only the first word of a hostname before the dot
What detail can Hubble see on Mars?
Can I use LPGL3 for library and Apache 2 for "main()"?
How to get the decimal part of a number in apex
Convert Numbers To Emoji Math
What chord could the notes 'F A♭ E♭' form?
GitLab account hacked and repo wiped
How do I minimise waste on a flight?
Why is my arithmetic with a long long int behaving this way?
Why is “using namespace std” considered bad practice?Why isnt int pow(int base, int exponent) in the standard C++ libraries?Why use static_cast<int>(x) instead of (int)x?Why can templates only be implemented in the header file?What does the C++ standard state the size of int, long type to be?What is the difference between const int*, const int * const, and int const *?Why is “using namespace std” considered bad practice?Easiest way to convert int to string in C++Why are elementwise additions much faster in separate loops than in a combined loop?Why is reading lines from stdin much slower in C++ than Python?Why is it faster to process a sorted array than an unsorted array?Why should I use a pointer rather than the object itself?
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty height:90px;width:728px;box-sizing:border-box;
I'm trying to calculate large integers with the long long datatype but when it gets large enough (2^55), the arithmetic behavior is unpredictable. I am working in Microsoft Visual Studio 2017.
In this first case, I am subtracting 2 from the long long variable m in the initialization. This works fine for all n until I try 54, then m will simply not be subtracted by 2.
#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
using namespace std;
#define LL long long
int main()
LL n;
cin >> n;
LL m = pow(2, n + 1) - 2;
cout << m;
return 0;
However, using this code m does get subtracted by 2 and is working as I would expect.
#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
using namespace std;
#define LL long long
int main()
LL n;
cin >> n;
LL m = pow(2, n + 1);
m -= 2;
cout << m;
return 0;
I expect both codes to be equivalent, why is this not the case?
c++ long-long
New contributor
Edvin K is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
|
show 5 more comments
I'm trying to calculate large integers with the long long datatype but when it gets large enough (2^55), the arithmetic behavior is unpredictable. I am working in Microsoft Visual Studio 2017.
In this first case, I am subtracting 2 from the long long variable m in the initialization. This works fine for all n until I try 54, then m will simply not be subtracted by 2.
#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
using namespace std;
#define LL long long
int main()
LL n;
cin >> n;
LL m = pow(2, n + 1) - 2;
cout << m;
return 0;
However, using this code m does get subtracted by 2 and is working as I would expect.
#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
using namespace std;
#define LL long long
int main()
LL n;
cin >> n;
LL m = pow(2, n + 1);
m -= 2;
cout << m;
return 0;
I expect both codes to be equivalent, why is this not the case?
c++ long-long
New contributor
Edvin K is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
24
I'm curious where you picked up#define LL long long. I see it pretty often on this site, but I'm not aware of who or what is propagating it. Edit : Is it another one of those code golf habits?
– François Andrieux
May 3 at 13:54
12
using namespace std;is considered bad practice.
– L. F.
May 3 at 13:55
22
@Lucas butusing LL = long longis also compile time, same size to type, and better. There is no reason to use a macro there
– Guillaume Racicot
May 3 at 14:04
20
@pipe: We understand perfectly well why. We don't want to promote bad practices just because you think it's "magically fine" for code examples. There are plenty of code examples on this site that were broken byusing namespace std;
– Mooing Duck
May 3 at 16:56
10
@ThomasMatthews when you're telling people to prefer bit-shift for signed integer types, please alert them that for signed integer types overflow is undefined behavior, that this is not just theoretical undefined behavior but can create actual bugs on common compilers likegcc, and that the numerical result can be surprising and unexpected if you don't consciously think about overflow even when it is compiled to behave exactly as expected for a fixed-width integer type.
– mtraceur
May 3 at 21:13
|
show 5 more comments
I'm trying to calculate large integers with the long long datatype but when it gets large enough (2^55), the arithmetic behavior is unpredictable. I am working in Microsoft Visual Studio 2017.
In this first case, I am subtracting 2 from the long long variable m in the initialization. This works fine for all n until I try 54, then m will simply not be subtracted by 2.
#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
using namespace std;
#define LL long long
int main()
LL n;
cin >> n;
LL m = pow(2, n + 1) - 2;
cout << m;
return 0;
However, using this code m does get subtracted by 2 and is working as I would expect.
#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
using namespace std;
#define LL long long
int main()
LL n;
cin >> n;
LL m = pow(2, n + 1);
m -= 2;
cout << m;
return 0;
I expect both codes to be equivalent, why is this not the case?
c++ long-long
New contributor
Edvin K is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
I'm trying to calculate large integers with the long long datatype but when it gets large enough (2^55), the arithmetic behavior is unpredictable. I am working in Microsoft Visual Studio 2017.
In this first case, I am subtracting 2 from the long long variable m in the initialization. This works fine for all n until I try 54, then m will simply not be subtracted by 2.
#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
using namespace std;
#define LL long long
int main()
LL n;
cin >> n;
LL m = pow(2, n + 1) - 2;
cout << m;
return 0;
However, using this code m does get subtracted by 2 and is working as I would expect.
#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
using namespace std;
#define LL long long
int main()
LL n;
cin >> n;
LL m = pow(2, n + 1);
m -= 2;
cout << m;
return 0;
I expect both codes to be equivalent, why is this not the case?
c++ long-long
c++ long-long
New contributor
Edvin K is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
New contributor
Edvin K is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
edited 2 days ago
vaxquis
7,90354058
7,90354058
New contributor
Edvin K is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
asked May 3 at 13:42
Edvin KEdvin K
914
914
New contributor
Edvin K is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
New contributor
Edvin K is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
24
I'm curious where you picked up#define LL long long. I see it pretty often on this site, but I'm not aware of who or what is propagating it. Edit : Is it another one of those code golf habits?
– François Andrieux
May 3 at 13:54
12
using namespace std;is considered bad practice.
– L. F.
May 3 at 13:55
22
@Lucas butusing LL = long longis also compile time, same size to type, and better. There is no reason to use a macro there
– Guillaume Racicot
May 3 at 14:04
20
@pipe: We understand perfectly well why. We don't want to promote bad practices just because you think it's "magically fine" for code examples. There are plenty of code examples on this site that were broken byusing namespace std;
– Mooing Duck
May 3 at 16:56
10
@ThomasMatthews when you're telling people to prefer bit-shift for signed integer types, please alert them that for signed integer types overflow is undefined behavior, that this is not just theoretical undefined behavior but can create actual bugs on common compilers likegcc, and that the numerical result can be surprising and unexpected if you don't consciously think about overflow even when it is compiled to behave exactly as expected for a fixed-width integer type.
– mtraceur
May 3 at 21:13
|
show 5 more comments
24
I'm curious where you picked up#define LL long long. I see it pretty often on this site, but I'm not aware of who or what is propagating it. Edit : Is it another one of those code golf habits?
– François Andrieux
May 3 at 13:54
12
using namespace std;is considered bad practice.
– L. F.
May 3 at 13:55
22
@Lucas butusing LL = long longis also compile time, same size to type, and better. There is no reason to use a macro there
– Guillaume Racicot
May 3 at 14:04
20
@pipe: We understand perfectly well why. We don't want to promote bad practices just because you think it's "magically fine" for code examples. There are plenty of code examples on this site that were broken byusing namespace std;
– Mooing Duck
May 3 at 16:56
10
@ThomasMatthews when you're telling people to prefer bit-shift for signed integer types, please alert them that for signed integer types overflow is undefined behavior, that this is not just theoretical undefined behavior but can create actual bugs on common compilers likegcc, and that the numerical result can be surprising and unexpected if you don't consciously think about overflow even when it is compiled to behave exactly as expected for a fixed-width integer type.
– mtraceur
May 3 at 21:13
24
24
I'm curious where you picked up
#define LL long long. I see it pretty often on this site, but I'm not aware of who or what is propagating it. Edit : Is it another one of those code golf habits?– François Andrieux
May 3 at 13:54
I'm curious where you picked up
#define LL long long. I see it pretty often on this site, but I'm not aware of who or what is propagating it. Edit : Is it another one of those code golf habits?– François Andrieux
May 3 at 13:54
12
12
using namespace std; is considered bad practice.– L. F.
May 3 at 13:55
using namespace std; is considered bad practice.– L. F.
May 3 at 13:55
22
22
@Lucas but
using LL = long long is also compile time, same size to type, and better. There is no reason to use a macro there– Guillaume Racicot
May 3 at 14:04
@Lucas but
using LL = long long is also compile time, same size to type, and better. There is no reason to use a macro there– Guillaume Racicot
May 3 at 14:04
20
20
@pipe: We understand perfectly well why. We don't want to promote bad practices just because you think it's "magically fine" for code examples. There are plenty of code examples on this site that were broken by
using namespace std;– Mooing Duck
May 3 at 16:56
@pipe: We understand perfectly well why. We don't want to promote bad practices just because you think it's "magically fine" for code examples. There are plenty of code examples on this site that were broken by
using namespace std;– Mooing Duck
May 3 at 16:56
10
10
@ThomasMatthews when you're telling people to prefer bit-shift for signed integer types, please alert them that for signed integer types overflow is undefined behavior, that this is not just theoretical undefined behavior but can create actual bugs on common compilers like
gcc, and that the numerical result can be surprising and unexpected if you don't consciously think about overflow even when it is compiled to behave exactly as expected for a fixed-width integer type.– mtraceur
May 3 at 21:13
@ThomasMatthews when you're telling people to prefer bit-shift for signed integer types, please alert them that for signed integer types overflow is undefined behavior, that this is not just theoretical undefined behavior but can create actual bugs on common compilers like
gcc, and that the numerical result can be surprising and unexpected if you don't consciously think about overflow even when it is compiled to behave exactly as expected for a fixed-width integer type.– mtraceur
May 3 at 21:13
|
show 5 more comments
3 Answers
3
active
oldest
votes
The issue with
LL m = pow(2, n + 1) - 2;
is that pow(2, n + 1) is not a long long. It has the type double and because the value is so large, subtracting 2 from it will not change its value. That means that m will not have the correct value. As you have already found, you need to assign the result first and then do the subtraction. Another alternative is to write your own pow that will return a integer type when given an integer type so you can do the raising to the power and subtraction at the same time.
8
For the OP: You should use bit shifting instead ofpow()function. Something like(1 << (n + 1)).
– Thomas Matthews
May 3 at 14:17
1
@StackDanny The problem is atpow(2, 54), notpow(2, 53)
– NathanOliver
May 3 at 14:24
5
Good answer but you should probably mention that the reason it works in the second example is thatpow(2,n)"accidentally" fits exactly in the most commonly used floating point representation, even for very large results, so there is never a round-off error involved.
– pipe
May 3 at 14:55
2
@pipe, for powers of two and binary computers, it's not an "accident" in any way.
– ilkkachu
May 3 at 18:32
2
@ThomasMatthews you'll need1ULL << (n + 1)to shift more than 31 bits
– phuclv
2 days ago
|
show 5 more comments
I expect both codes to be equivalent, why is this not the case?
Your expectation is wrong. Your second code would be equivalent to this:
auto m = static_cast<LL>( pow(2, n + 1) ) - 2;
as due to conversion rule for arithmetic operators and the fact that std::pow() returns double in this case:
For the binary operators (except shifts), if the promoted operands have different types, additional set of implicit conversions is applied, known as usual arithmetic conversions with the goal to produce the common type (also accessible via the std::common_type type trait). If, prior to any integral promotion, one operand is of enumeration type and the other operand is of a floating-point type or a different enumeration type, this behavior is deprecated. (since C++20)
If either operand has scoped enumeration type, no conversion is performed: the other operand and the return type must have the same type
Otherwise, if either operand is long double, the other operand is converted to long double
Otherwise, if either operand is double, the other operand is converted to double
Otherwise, if either operand is float, the other operand is converted to float
...
(emphasis is mine) your original expression would lead to double - double instead of long long int - long long int as you do in the second case hence the difference.
add a comment |
The pow function returns a value of type double, which only has 53 bits of precision. While the returned value will fit in a double even if n is greater than 53, subtracting 2 results in a value of type double that requires more than 53 bits of precision so the result of the subtraction is rounded to the nearest representable value.
The reason breaking out the subtraction works is because the double value returned from pow is assigned to a long long, then you subtract an int from a long long.
Since you're not dealing with floating point numbers and you're only raising 2 to a power, you can replace the call to pow with a simple left shift:
LL m = (1LL << (n + 1)) - 2;
This keeps all intermediate values at type long long.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Edvin K is a new contributor. Be nice, and check out our Code of Conduct.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55971072%2fwhy-is-my-arithmetic-with-a-long-long-int-behaving-this-way%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
The issue with
LL m = pow(2, n + 1) - 2;
is that pow(2, n + 1) is not a long long. It has the type double and because the value is so large, subtracting 2 from it will not change its value. That means that m will not have the correct value. As you have already found, you need to assign the result first and then do the subtraction. Another alternative is to write your own pow that will return a integer type when given an integer type so you can do the raising to the power and subtraction at the same time.
8
For the OP: You should use bit shifting instead ofpow()function. Something like(1 << (n + 1)).
– Thomas Matthews
May 3 at 14:17
1
@StackDanny The problem is atpow(2, 54), notpow(2, 53)
– NathanOliver
May 3 at 14:24
5
Good answer but you should probably mention that the reason it works in the second example is thatpow(2,n)"accidentally" fits exactly in the most commonly used floating point representation, even for very large results, so there is never a round-off error involved.
– pipe
May 3 at 14:55
2
@pipe, for powers of two and binary computers, it's not an "accident" in any way.
– ilkkachu
May 3 at 18:32
2
@ThomasMatthews you'll need1ULL << (n + 1)to shift more than 31 bits
– phuclv
2 days ago
|
show 5 more comments
The issue with
LL m = pow(2, n + 1) - 2;
is that pow(2, n + 1) is not a long long. It has the type double and because the value is so large, subtracting 2 from it will not change its value. That means that m will not have the correct value. As you have already found, you need to assign the result first and then do the subtraction. Another alternative is to write your own pow that will return a integer type when given an integer type so you can do the raising to the power and subtraction at the same time.
8
For the OP: You should use bit shifting instead ofpow()function. Something like(1 << (n + 1)).
– Thomas Matthews
May 3 at 14:17
1
@StackDanny The problem is atpow(2, 54), notpow(2, 53)
– NathanOliver
May 3 at 14:24
5
Good answer but you should probably mention that the reason it works in the second example is thatpow(2,n)"accidentally" fits exactly in the most commonly used floating point representation, even for very large results, so there is never a round-off error involved.
– pipe
May 3 at 14:55
2
@pipe, for powers of two and binary computers, it's not an "accident" in any way.
– ilkkachu
May 3 at 18:32
2
@ThomasMatthews you'll need1ULL << (n + 1)to shift more than 31 bits
– phuclv
2 days ago
|
show 5 more comments
The issue with
LL m = pow(2, n + 1) - 2;
is that pow(2, n + 1) is not a long long. It has the type double and because the value is so large, subtracting 2 from it will not change its value. That means that m will not have the correct value. As you have already found, you need to assign the result first and then do the subtraction. Another alternative is to write your own pow that will return a integer type when given an integer type so you can do the raising to the power and subtraction at the same time.
The issue with
LL m = pow(2, n + 1) - 2;
is that pow(2, n + 1) is not a long long. It has the type double and because the value is so large, subtracting 2 from it will not change its value. That means that m will not have the correct value. As you have already found, you need to assign the result first and then do the subtraction. Another alternative is to write your own pow that will return a integer type when given an integer type so you can do the raising to the power and subtraction at the same time.
edited May 3 at 14:48
Medinoc
5,9291230
5,9291230
answered May 3 at 13:47
NathanOliverNathanOliver
101k17142225
101k17142225
8
For the OP: You should use bit shifting instead ofpow()function. Something like(1 << (n + 1)).
– Thomas Matthews
May 3 at 14:17
1
@StackDanny The problem is atpow(2, 54), notpow(2, 53)
– NathanOliver
May 3 at 14:24
5
Good answer but you should probably mention that the reason it works in the second example is thatpow(2,n)"accidentally" fits exactly in the most commonly used floating point representation, even for very large results, so there is never a round-off error involved.
– pipe
May 3 at 14:55
2
@pipe, for powers of two and binary computers, it's not an "accident" in any way.
– ilkkachu
May 3 at 18:32
2
@ThomasMatthews you'll need1ULL << (n + 1)to shift more than 31 bits
– phuclv
2 days ago
|
show 5 more comments
8
For the OP: You should use bit shifting instead ofpow()function. Something like(1 << (n + 1)).
– Thomas Matthews
May 3 at 14:17
1
@StackDanny The problem is atpow(2, 54), notpow(2, 53)
– NathanOliver
May 3 at 14:24
5
Good answer but you should probably mention that the reason it works in the second example is thatpow(2,n)"accidentally" fits exactly in the most commonly used floating point representation, even for very large results, so there is never a round-off error involved.
– pipe
May 3 at 14:55
2
@pipe, for powers of two and binary computers, it's not an "accident" in any way.
– ilkkachu
May 3 at 18:32
2
@ThomasMatthews you'll need1ULL << (n + 1)to shift more than 31 bits
– phuclv
2 days ago
8
8
For the OP: You should use bit shifting instead of
pow() function. Something like (1 << (n + 1)).– Thomas Matthews
May 3 at 14:17
For the OP: You should use bit shifting instead of
pow() function. Something like (1 << (n + 1)).– Thomas Matthews
May 3 at 14:17
1
1
@StackDanny The problem is at
pow(2, 54), not pow(2, 53)– NathanOliver
May 3 at 14:24
@StackDanny The problem is at
pow(2, 54), not pow(2, 53)– NathanOliver
May 3 at 14:24
5
5
Good answer but you should probably mention that the reason it works in the second example is that
pow(2,n) "accidentally" fits exactly in the most commonly used floating point representation, even for very large results, so there is never a round-off error involved.– pipe
May 3 at 14:55
Good answer but you should probably mention that the reason it works in the second example is that
pow(2,n) "accidentally" fits exactly in the most commonly used floating point representation, even for very large results, so there is never a round-off error involved.– pipe
May 3 at 14:55
2
2
@pipe, for powers of two and binary computers, it's not an "accident" in any way.
– ilkkachu
May 3 at 18:32
@pipe, for powers of two and binary computers, it's not an "accident" in any way.
– ilkkachu
May 3 at 18:32
2
2
@ThomasMatthews you'll need
1ULL << (n + 1) to shift more than 31 bits– phuclv
2 days ago
@ThomasMatthews you'll need
1ULL << (n + 1) to shift more than 31 bits– phuclv
2 days ago
|
show 5 more comments
I expect both codes to be equivalent, why is this not the case?
Your expectation is wrong. Your second code would be equivalent to this:
auto m = static_cast<LL>( pow(2, n + 1) ) - 2;
as due to conversion rule for arithmetic operators and the fact that std::pow() returns double in this case:
For the binary operators (except shifts), if the promoted operands have different types, additional set of implicit conversions is applied, known as usual arithmetic conversions with the goal to produce the common type (also accessible via the std::common_type type trait). If, prior to any integral promotion, one operand is of enumeration type and the other operand is of a floating-point type or a different enumeration type, this behavior is deprecated. (since C++20)
If either operand has scoped enumeration type, no conversion is performed: the other operand and the return type must have the same type
Otherwise, if either operand is long double, the other operand is converted to long double
Otherwise, if either operand is double, the other operand is converted to double
Otherwise, if either operand is float, the other operand is converted to float
...
(emphasis is mine) your original expression would lead to double - double instead of long long int - long long int as you do in the second case hence the difference.
add a comment |
I expect both codes to be equivalent, why is this not the case?
Your expectation is wrong. Your second code would be equivalent to this:
auto m = static_cast<LL>( pow(2, n + 1) ) - 2;
as due to conversion rule for arithmetic operators and the fact that std::pow() returns double in this case:
For the binary operators (except shifts), if the promoted operands have different types, additional set of implicit conversions is applied, known as usual arithmetic conversions with the goal to produce the common type (also accessible via the std::common_type type trait). If, prior to any integral promotion, one operand is of enumeration type and the other operand is of a floating-point type or a different enumeration type, this behavior is deprecated. (since C++20)
If either operand has scoped enumeration type, no conversion is performed: the other operand and the return type must have the same type
Otherwise, if either operand is long double, the other operand is converted to long double
Otherwise, if either operand is double, the other operand is converted to double
Otherwise, if either operand is float, the other operand is converted to float
...
(emphasis is mine) your original expression would lead to double - double instead of long long int - long long int as you do in the second case hence the difference.
add a comment |
I expect both codes to be equivalent, why is this not the case?
Your expectation is wrong. Your second code would be equivalent to this:
auto m = static_cast<LL>( pow(2, n + 1) ) - 2;
as due to conversion rule for arithmetic operators and the fact that std::pow() returns double in this case:
For the binary operators (except shifts), if the promoted operands have different types, additional set of implicit conversions is applied, known as usual arithmetic conversions with the goal to produce the common type (also accessible via the std::common_type type trait). If, prior to any integral promotion, one operand is of enumeration type and the other operand is of a floating-point type or a different enumeration type, this behavior is deprecated. (since C++20)
If either operand has scoped enumeration type, no conversion is performed: the other operand and the return type must have the same type
Otherwise, if either operand is long double, the other operand is converted to long double
Otherwise, if either operand is double, the other operand is converted to double
Otherwise, if either operand is float, the other operand is converted to float
...
(emphasis is mine) your original expression would lead to double - double instead of long long int - long long int as you do in the second case hence the difference.
I expect both codes to be equivalent, why is this not the case?
Your expectation is wrong. Your second code would be equivalent to this:
auto m = static_cast<LL>( pow(2, n + 1) ) - 2;
as due to conversion rule for arithmetic operators and the fact that std::pow() returns double in this case:
For the binary operators (except shifts), if the promoted operands have different types, additional set of implicit conversions is applied, known as usual arithmetic conversions with the goal to produce the common type (also accessible via the std::common_type type trait). If, prior to any integral promotion, one operand is of enumeration type and the other operand is of a floating-point type or a different enumeration type, this behavior is deprecated. (since C++20)
If either operand has scoped enumeration type, no conversion is performed: the other operand and the return type must have the same type
Otherwise, if either operand is long double, the other operand is converted to long double
Otherwise, if either operand is double, the other operand is converted to double
Otherwise, if either operand is float, the other operand is converted to float
...
(emphasis is mine) your original expression would lead to double - double instead of long long int - long long int as you do in the second case hence the difference.
edited May 3 at 14:26
answered May 3 at 14:17
SlavaSlava
33.4k12968
33.4k12968
add a comment |
add a comment |
The pow function returns a value of type double, which only has 53 bits of precision. While the returned value will fit in a double even if n is greater than 53, subtracting 2 results in a value of type double that requires more than 53 bits of precision so the result of the subtraction is rounded to the nearest representable value.
The reason breaking out the subtraction works is because the double value returned from pow is assigned to a long long, then you subtract an int from a long long.
Since you're not dealing with floating point numbers and you're only raising 2 to a power, you can replace the call to pow with a simple left shift:
LL m = (1LL << (n + 1)) - 2;
This keeps all intermediate values at type long long.
add a comment |
The pow function returns a value of type double, which only has 53 bits of precision. While the returned value will fit in a double even if n is greater than 53, subtracting 2 results in a value of type double that requires more than 53 bits of precision so the result of the subtraction is rounded to the nearest representable value.
The reason breaking out the subtraction works is because the double value returned from pow is assigned to a long long, then you subtract an int from a long long.
Since you're not dealing with floating point numbers and you're only raising 2 to a power, you can replace the call to pow with a simple left shift:
LL m = (1LL << (n + 1)) - 2;
This keeps all intermediate values at type long long.
add a comment |
The pow function returns a value of type double, which only has 53 bits of precision. While the returned value will fit in a double even if n is greater than 53, subtracting 2 results in a value of type double that requires more than 53 bits of precision so the result of the subtraction is rounded to the nearest representable value.
The reason breaking out the subtraction works is because the double value returned from pow is assigned to a long long, then you subtract an int from a long long.
Since you're not dealing with floating point numbers and you're only raising 2 to a power, you can replace the call to pow with a simple left shift:
LL m = (1LL << (n + 1)) - 2;
This keeps all intermediate values at type long long.
The pow function returns a value of type double, which only has 53 bits of precision. While the returned value will fit in a double even if n is greater than 53, subtracting 2 results in a value of type double that requires more than 53 bits of precision so the result of the subtraction is rounded to the nearest representable value.
The reason breaking out the subtraction works is because the double value returned from pow is assigned to a long long, then you subtract an int from a long long.
Since you're not dealing with floating point numbers and you're only raising 2 to a power, you can replace the call to pow with a simple left shift:
LL m = (1LL << (n + 1)) - 2;
This keeps all intermediate values at type long long.
answered May 3 at 15:33
dbushdbush
106k15111150
106k15111150
add a comment |
add a comment |
Edvin K is a new contributor. Be nice, and check out our Code of Conduct.
Edvin K is a new contributor. Be nice, and check out our Code of Conduct.
Edvin K is a new contributor. Be nice, and check out our Code of Conduct.
Edvin K is a new contributor. Be nice, and check out our Code of Conduct.
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55971072%2fwhy-is-my-arithmetic-with-a-long-long-int-behaving-this-way%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
24
I'm curious where you picked up
#define LL long long. I see it pretty often on this site, but I'm not aware of who or what is propagating it. Edit : Is it another one of those code golf habits?– François Andrieux
May 3 at 13:54
12
using namespace std;is considered bad practice.– L. F.
May 3 at 13:55
22
@Lucas but
using LL = long longis also compile time, same size to type, and better. There is no reason to use a macro there– Guillaume Racicot
May 3 at 14:04
20
@pipe: We understand perfectly well why. We don't want to promote bad practices just because you think it's "magically fine" for code examples. There are plenty of code examples on this site that were broken by
using namespace std;– Mooing Duck
May 3 at 16:56
10
@ThomasMatthews when you're telling people to prefer bit-shift for signed integer types, please alert them that for signed integer types overflow is undefined behavior, that this is not just theoretical undefined behavior but can create actual bugs on common compilers like
gcc, and that the numerical result can be surprising and unexpected if you don't consciously think about overflow even when it is compiled to behave exactly as expected for a fixed-width integer type.– mtraceur
May 3 at 21:13