Why does my while loop not work for this specific data-set?
1 次查看(过去 30 天)
显示 更早的评论
I have written the following function, that effectively adjusts rounding errors. The code appears to get stuck in a while loop despite the condition of sum(roundedAssetAllocation) == 1. Could someone help to explain why?
function [a, n] = fixRounding(x, s)
u = 10.^-s;
n = length(x);
assetAllocation = x/sum(x);
roundedAssetAllocation = round(assetAllocation, s);
while sum(roundedAssetAllocation) ~= 1
diffFromUnrounded = assetAllocation - roundedAssetAllocation;
if sum(roundedAssetAllocation) < 1
[~, index] = max(diffFromUnrounded);
roundedAssetAllocation(index) = roundedAssetAllocation(index) + u;
elseif sum(roundedAssetAllocation) > 1
[~, index] = min(diffFromUnrounded);
roundedAssetAllocation(index) = roundedAssetAllocation(index) - u;
end
a = roundedAssetAllocation;
end
end
The following call should return: [.3485, .1198, .1356, .1761, .2200], but it instead gets stuck in a loop.
fixRounding([0.837746777686794, 0.287856637640281, 0.325810457772455, 0.423276428672838, 0.528787529659317], 4)
0 个评论
采纳的回答
Guillaume
2017-2-20
编辑:Guillaume
2017-2-20
First, note that there is a bug in your code. If the while loop never get entered (if the sum is already exactly 1), then a never gets created.
The problem is a basic one of floating point operations. It is extremely difficult to equate the sum of numbers to a given number due to the precision of floating point numbers.
Had you stepped through your code you would have seen that on the 2nd iteration of the loop, the sum differs from 1 by only 2.2204e-16. No matter how much 0.0001 you add or subtract you're never going to get to one.
The solution would be to round the sum before comparing to one. Since the smallest value you add or subtract is 1e-s. round the sum to s decimals:
while round(sum(roundedAssetAllocation), s) ~= 1
And never use == or ~= to compare unrounded floating point numbers. round the numbers or use a small tolerance:
while abs(sum(roundedAssetAllocation) - 1) <= u
2 个评论
Guillaume
2017-2-20
Well, if stopping with an obscure error is purposely not run then I guess it's not a bug. Stopping with an error message that actually says the function should not be called when the sum is 1 would be much better in my opinion.
>>result = fixRoundind([1 2], 1)
Output argument "a" (and maybe others) not assigned during call to "fixRounding".
Adding
assert(sum(roundedAssetAllocation) == 1, 'Do not call this code when the normalised sum is already 1')
would make the error much clearer (and would avoid somebody else telling you you've got a bug).
更多回答(1 个)
Jan
2017-2-20
You cannot assume that the sum of some double values is exactly 1.0 only by varying the inputs by adding or subtracting 10.^-s. See the frequently asked question: Answers: why-is-0.3-0.2-0.1-not-equal-to-zero . This is an effect of storing decimal values in the binary IEEE754 format: The most decimal numbers do not have an exact binary representation and the conversion can create some rounding "errors".
The solution is to work with tolerances, but this is more or less arbitrary.
0 个评论
另请参阅
类别
在 Help Center 和 File Exchange 中查找有关 Matrix Indexing 的更多信息
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!