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

1 visualización (últimos 30 días)
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)

Respuesta aceptada

Guillaume
Guillaume el 20 de Feb. de 2017
Editada: Guillaume el 20 de Feb. de 2017
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 comentarios
Aaron Jean-Baptiste
Aaron Jean-Baptiste el 20 de Feb. de 2017
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 el 20 de Feb. de 2017
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).

Iniciar sesión para comentar.

Más respuestas (1)

Jan
Jan el 20 de Feb. de 2017
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.

Categorías

Más información sobre Matrix Indexing en Help Center y File Exchange.

Community Treasure Hunt

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

Start Hunting!

Translated by