Was ist mit gesundem Menschenverstand richtig: (int) blabla * 255.99999999999997 oder rund (blabla * 255)?

7

Vor kurzem fand ich diese interessante Sache in Webkit-Quellen, die sich auf Farbkonvertierungen beziehen (hsl to rgb):

Ссылка

%Vor%

Dasselbe habe ich hier gefunden: Ссылка

%Vor%

Stimmt es überhaupt, eine solche Technik zu verwenden? Warum benutzt man nicht gerade etwas rundes (blabla * 255)? Sind es Features von C / C ++? Wie ich genau sehe, gibt es in 27 Fällen von 100 nicht immer korrekte Ergebnisse. Siehe Tabelle unter Ссылка

Jemand bitte erklären - ich denke, es sollte etwas Grundlegendes sein.

    
gaRex 11.03.2014, 19:09
quelle

3 Antworten

25

Normalerweise wollen wir einen realen Wert x im (geschlossenen) Intervall [0,1] auf einen ganzzahligen Wert j im Bereich [0 ...255] abbilden.

Und wir wollen es auf eine "faire" Art und Weise tun, so dass die diskreten Werte ungefähr gleich sind, wenn die Realwerte gleichmäßig in dem Bereich verteilt sind: Jeder der 256 diskreten Werte sollte "den gleichen Anteil" erhalten. (1/256) aus dem [0,1] Intervall. Das heißt, wir wollen eine Zuordnung wie folgt:

%Vor%

Wir sind nicht sehr besorgt über die Übergangspunkte [*], aber wir wollen den vollen Bereich [0,1] abdecken. Wie erreiche ich das?

Wenn wir einfach j = (int)(x *255) machen: Der Wert 255 würde fast nie erscheinen (nur wenn x=1 ); und der Rest der Werte 0...254 würde jeweils einen Anteil von 1/255 des Intervalls erhalten. Dies wäre unfair, ungeachtet des Rundungsverhaltens an den Grenzpunkten.

Wenn wir stattdessen j = (int)(x * 256) verwenden: Diese Partition wäre fair, außer für ein kleines Problem: wir würden den Wert 256 (außerhalb des Bereichs!) erhalten, wenn x=1 [**]

Das ist der Grund, warum j = (int)(x * 255.9999...) (wobei 255.9999... tatsächlich der größte Doppelwert unter 256 ist) ausreichen wird.

Eine alternative Implementierung (auch sinnvoll, fast gleichwertig) wäre

%Vor%

aber das wäre ungeschickter und wahrscheinlich weniger effizient.

round() hilft hier nicht. Zum Beispiel würde j = (int)round(x * 255) den Ganzzahlen j=1...254 einen Anteil von 1/255 und den Extremwerten j=0 , j=255 den halben Wert geben.

[*] Ich meine: Wir sind nicht sehr interessiert an dem, was in der "kleinen" Nachbarschaft von, sagen wir, 3/256 passiert: Rundung könnte 2 oder 3 ergeben, es ist egal. Aber wir sind an den Extrema interessiert: Wir wollen 0 und 255 für x=0 bzw. x=1 erhalten.

[**] Der IEEE-Gleitkomma-Standard garantiert, dass es hier keine Rundungsmehrdeutigkeit gibt: Ganzzahlen erlauben eine exakte Gleitkommadarstellung, das Produkt wird exakt sein und das Casting wird immer 256 ergeben. Außerdem ist garantiert, dass 1.0 * z = z .

    
leonbloy 11.03.2014, 19:20
quelle
7

Im Allgemeinen würde ich sagen, dass (int)(blabla * 255.99999999999997) korrekter ist als round() .

Warum?

Weil mit round() , 0 und 255 nur "die Hälfte" des Bereichs von 1-254 haben. Wenn Sie round() , dann werden 0-0.00196078431 auf 0 abgebildet, während 0.00196078431-0.00588235293 auf 1 abgebildet wird. Dies bedeutet, dass 1 eine 200% höhere Wahrscheinlichkeit hat, als 0 zu erscheinen, was streng genommen eine unfaire Verzerrung ist.

Wenn man stattdessen mit 255.99999999999997 und dann floors multipliziert (was bei einer Ganzzahl der Fall ist, da es abgeschnitten wird), dann ist jede ganze Zahl von 0 bis 255 gleich wahrscheinlich.

Ihr Arbeitsblatt könnte dies besser anzeigen, wenn es in gebrochenen Prozentsätzen gezählt wird (d. h. wenn es jedes Mal um 0,01% anstatt um 1% gezählt wird). Ich habe eine einfache Tabelle erstellt, um dies anzuzeigen . Wenn Sie sich diese Tabelle anschauen, werden Sie sehen, dass 0 unfairerweise voreingenommen ist, wenn round() ing ist, aber mit der anderen Methode sind die Dinge fair und gleich.

    
Cornstalks 11.03.2014 19:25
quelle
5

Casting to int hat den gleichen Effekt wie die floor-Funktion (d. h. es schneidet ab). Wenn Sie es umrunden, wird auf die nächste Ganzzahl gerundet.

Sie machen verschiedene Dinge, also wähle die, die du brauchst.

    
rubenvb 11.03.2014 19:14
quelle

Tags und Links