Why does my while loop not work for this specific data-set?

1 回表示 (過去 30 日間)
Aaron Jean-Baptiste
Aaron Jean-Baptiste 2017 年 2 月 20 日
コメント済み: Guillaume 2017 年 2 月 20 日
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)

採用された回答

Guillaume
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 件のコメント
Aaron Jean-Baptiste
Aaron Jean-Baptiste 2017 年 2 月 20 日
That is not a bug in the code; it should purposely not run when:
sum(roundAssetAllocation) == 1.
It appears it was due to float numbers and using:
while round(sum(roundedAssetAllocation), s) ~= 1
Fixed my problem.
Thank you very much,
Aaron
Guillaume
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
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.

カテゴリ

Help Center および File ExchangeMatrix Indexing についてさらに検索

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by