Java'daki şamandıraları karşılaştırmak için == kullanmanın nesi yanlış?


177

Bu java.sun sayfasına göre ==Java'da kayan nokta sayıları için eşitlik karşılaştırma operatörüdür.

Ancak, bu kodu yazarken:

if(sectionID == currentSectionID)

benim editörü içine ve statik analiz çalıştırmak, elde: "JAVA0078 = = ile karşılaştırıldığında kayan nokta değerleri"

==Kayan nokta değerlerini karşılaştırmak için kullanmanın yanlışlığı nedir ? Bunu yapmanın doğru yolu nedir? 


29
Şamandıraları == ile karşılaştırmak sorunlu olduğu için bunları kimlik olarak kullanmak akıllıca değildir; örnek kodunuzdaki adlar, yaptığınız şeyin bu olduğunu gösterir; uzun tamsayılar (uzunlar) ve kimlikler için fiili standart tercih edilir.
Carl Manaster


4
Evet, bu sadece rastgele bir örnek miydi yoksa gerçekte kimlikler olarak şamandıralar mı kullanıyorsunuz? Bir sebebi var mı?
Wiklander başına

5
"şamandıra alanları için Float.compare yöntemini kullanın ve çift alanlar için Double.compare kullanın. ayrıntılar için Float.equals belgelerine bakın. " (Joshua Bloch: Etkili Java)
lbalazscs

Yanıtlar:


211

şamandıraları 'eşitlik' için test etmenin doğru yolu:

if(Math.abs(sectionID - currentSectionID) < epsilon)

Burada epsilon, istenen hassasiyete bağlı olarak 0.00000001 gibi çok küçük bir sayıdır.


26
Sabit bir epsilonun neden her zaman iyi bir fikir olmadığını öğrenmek için kabul edilen yanıttaki bağlantıya bakın ( cygnus-software.com/papers/comparingfloats/comparingfloats.htm ). Spesifik olarak, karşılaştırılan şamandıralardaki değerler büyüdükçe (veya küçüldükçe), epsilon artık uygun değildir. (Şamandıra değerlerinizin nispeten makul olduğunu biliyorsanız, epsilon kullanmak iyidir.)
PT

1
@PT EPSilon'u bir sayıyla çarpabilir ve if(Math.abs(sectionID - currentSectionID) < epsilon*sectionIDbu sorunu çözmek için işlevi değiştirebilir mi?
enthusiasticgeek

3
Bu şimdiye kadarki en iyi cevap bile olabilir, ama yine de kusurlu. EPSilon'u nereden alıyorsunuz?
Michael Piefel

1
@MichaelPiefel zaten “istenilen hassasiyete bağlı” diyor. Doğası gereği yüzer türler fiziksel değerlere benzer: sadece toplam yanlışlığa bağlı olarak sınırlı sayıda konumla ilgileniyorsunuz, bunun ötesinde herhangi bir farklılık moot olarak kabul ediliyor.
ivan_pozdeev

Ancak OP gerçekten sadece eşitliği test etmek istiyordu ve bunun güvenilir olmadığı bilindiğinden farklı bir yöntem kullanmak zorunda. Yine de, “arzu edilen hassasiyetinin” ne olduğunu bile bildiğinden emin değilim; yani istediğiniz tek şey daha güvenilir bir eşitlik testi ise, soru şu: Epsilon'u nereden alıyorsunuz? Math.ulp()Bu soruya verdiğim cevabı kullanmayı önerdim .
Michael Piefel

53

Kayan nokta değerleri bir miktar kapalı olabilir, bu nedenle tam olarak eşit olarak rapor edilmeyebilirler. Örneğin, bir kayan noktayı "6.1" olarak ayarlayıp tekrar basmak, "6.099999904632568359375" gibi bir şeyin rapor edilen değerini alabilirsiniz. Bu, şamandıraların çalışması için esastır; bu nedenle, onları eşitlik kullanarak karşılaştırmak istemezsiniz, daha ziyade bir aralık içindeki karşılaştırma, yani şamandıranın karşılaştırmak istediğiniz sayıya olan farkı belirli bir mutlak değerden daha azsa.

Kayıttaki bu makale, durumun neden böyle olduğuna dair iyi bir genel bakış sunmaktadır; yararlı ve ilginç okuma.


@kevindtimm: eşitlik testlerinizi şu şekilde yapacaksınız: (sayı == 6.099999904632568359375) sayıyı bilmek istediğiniz her zaman 6.1'e eşitse ... Evet haklısınız ... bilgisayardaki her şey kesinlikle belirleyicidir, sadece yüzer problemler için kullanılan yaklaşımların matematik problemleri karşısında sezgisel olması.
Newtopian

Kayan nokta değerleri, çok özel donanımlarda yalnızca belirsiz olarak kesin değildir .
Stuart P. Bentley

1
@Stuart Yanılmış olabilirim, ama FDIV hatasının deterministik olmadığını düşünüyorum. Donanım tarafından verilen cevaplar spesifikasyona uygun değildi, ancak aynı hesaplama her zaman aynı yanlış sonucu verdiğinden belirleyici idi
Gravity

@Gravity Belirli bir uyarı kümesi göz önüne alındığında herhangi bir davranışın deterministik olduğunu iddia edebilirsiniz.
Stuart P. Bentley

Kayan nokta değerleri kesin değildir. Her bir kayan nokta değeri tam olarak budur. Kesin olmayan, kayan nokta hesaplamasının sonucudur . Ama dikkat et! Bir programda 0.1 gibi bir şey gördüğünüzde, bu bir kayan nokta değeri değildir. Bu bir kayan nokta değişmezidir ; derleyici bir hesaplama yaparak kayan nokta değerine dönüştüren bir dize .
Solomon Slow

22

Sadece herkesin söylediklerinin arkasındaki sebebi belirtmek için.

Bir şamandıranın ikili temsili biraz sinir bozucudur.

İkili programcıların çoğu, 1b = 1d, 10b = 2d, 100b = 4d, 1000b = 8d arasındaki korelasyonu bilir

Peki başka bir şekilde çalışıyor.

.1b = .5d, .01b = .25d, .001b = .125, ...

Sorun, .1, .2, .3 vb. Gibi çoğu ondalık sayıyı temsil etmenin kesin bir yolu olmamasıdır. Sayılar yazdırıldığında sistem biraz yuvarlama yapar, böylece .10000000000001 veya .999999999999 yerine .1 görüntülenir (muhtemelen depolanan gösterime .1 kadar yakındır)

Yorumdan düzenle: Bunun bir sorun olmasının nedeni beklentilerimiz. .7 ya da .67 ya da .666667'yi ondalık sayıya dönüştürdüğümüzde 2/3'ün bir noktada tamamen yumuşatılmasını bekliyoruz. Ancak .1'in 2/3 ile aynı şekilde yuvarlanmasını beklemiyoruz. - ve tam olarak olan bu.

Bu arada, dahili olarak sakladığı sayıyı merak ediyorsanız, bir ikili "Bilimsel Gösterim" kullanan saf bir ikili gösterimdir. Eğer ondalık sayı 10.75d saklamasını söylediyseniz, 10 için 1010b ve ondalık için .11b saklar. Yani .101011'i saklardı ve sonunda birkaç bit kaydeder: Ondalık noktasını dört basamak sağa hareket ettirir.

(Teknik olarak artık ondalık bir nokta olmamasına rağmen, şimdi bir ikili nokta, ancak bu terminoloji, herhangi bir kullanımın bu cevabını bulan çoğu insan için işleri daha anlaşılır yapmazdı.)


1
@Matt K - um, sabit nokta değil; "ondalık noktasını [N] bitleri sağa hareket ettirmek için sonunda birkaç bit kaydederseniz", kayan noktadır. Sabit nokta, yarıçap noktasının konumunu sabit olarak alır. Ayrıca, genel olarak, binamal (?) Noktasının kaydırılması her zaman sizi en sol konumda bir '1' bırakacak şekilde yapılabileceğinden, bu şekilde kurtarılan alanı ayıran önde gelen '1'i atlayan bazı sistemler bulacaksınız (1 bit!) üs aralığını genişletmek için.
JustJeff

Problem ikili ve ondalık gösterimle ilgili değildir. Ondalık kayan nokta ile, hala (1/3) * 3 == 0.9999999999999999999999999999 gibi şeylere sahipsiniz.
dan04

2
@ dan04 evet, 1/3 ondalık VEYA ikili temsile sahip olmadığından, üçlü bir temsili vardır ve bu şekilde doğru bir şekilde dönüşür :). Listelediğim sayıların (.1, .25, vb.) Hepsi mükemmel ondalık temsillere sahip ancak ikili gösterim yok - ve insanlar "kesin" temsillere alışkın. BCD onları mükemmel idare ederdi. Aradaki fark bu.
Bill K

1
Sorunun arkasındaki GERÇEK problemi açıkladığı için bunun daha fazla oy hakkı olmalıdır.
Levite

19

Kayan nokta değerlerini karşılaştırmak için == kullanmanın sorunu nedir?

Çünkü bu doğru değil 0.1 + 0.2 == 0.3


7
Ne hakkında Float.compare(0.1f+0.2f, 0.3f) == 0?
Kova Gücü

0.1f + 0.2f == 0.3f ancak 0.1d + 0.2d! = 0.3d. Varsayılan olarak, 0.1 + 0.2 bir çifttir. 0.3 de bir çifttir.
burnabyRails

12

Bence şamandıralar (ve çiftler) etrafında çok fazla karışıklık var, onu temizlemek iyi.

  1. Standart uyumlu JVM'de şamandıraların kimlik olarak kullanılmasında doğası gereği yanlış bir şey yoktur [*]. Şamandıra kimliğini x olarak ayarladıysanız, onunla hiçbir şey yapmayın (yani aritmetik yok) ve daha sonra y == x için test edin, iyi olacaksınız. Ayrıca bunları HashMap'te anahtar olarak kullanmada yanlış bir şey yoktur. Yapamayacağınız şey x == (x - y) + y, vb. Eşitlikler olduğunu varsaymaktır . Buna göre, insanlar genellikle tamsayı tiplerini kimlik olarak kullanırlar ve buradaki çoğu insanın bu kod tarafından ertelendiğini gözlemleyebilirsiniz, bu yüzden pratik nedenlerle, sözleşmelere uymak daha iyidir . Uzun olduğu kadar çok farklı doubledeğer olduğuna dikkat edin values, böylece kullanarak hiçbir şey kazanamazsınız double. Ayrıca, "bir sonraki kullanılabilir kimlik" oluşturmak iki kat ile zor olabilir ve kayan nokta aritmetiği hakkında biraz bilgi gerektirir. Sorun değil.

  2. Öte yandan, iki matematiksel olarak denk hesaplamanın sonuçlarının sayısal eşitliğine güvenmek risklidir. Bunun nedeni, yuvarlama hataları ve ondalıktan ikili gösterime dönüştürürken hassasiyet kaybıdır. Bu SO üzerinde ölümle tartışıldı.

[*] “Standart uyumlu JVM” dediğimde, beyinden zarar görmüş bazı JVM uygulamalarını hariç tutmak istedim. Bkz bu .


Şamandıraları kimlik olarak kullanırken, ya bunun ==yerine kullanarak karşılaştırıldıklarından equalsya da kendisiyle eşit olmayan hiçbir şamandıranın bir tabloda saklanmadığından emin olunmalıdır. Aksi takdirde, örneğin çeşitli girdiler beslendiğinde bir ifadeden kaç tane benzersiz sonuç üretilebileceğini saymaya çalışan bir program her NaN değerini benzersiz olarak kabul edebilir.
supercat

Yukarıdakiler Float, değil float.
quant_dev

Neden bahsediyorsun Float? Biri benzersiz floatdeğerler tablosu oluşturmaya çalışır ve bunları karşılaştırırsa ==, korkunç IEEE-754 karşılaştırma kuralları tablonun NaNdeğerlerle dolmasına neden olur .
supercat

floattür equalsyöntemi yok.
quant_dev

Ah - Ben bir equalsörnek yöntemi değil, Floatiki tip değeri karşılaştıran statik yöntem ( sınıf içinde düşünüyorum ) demek istedim float.
supercat

9

Bu, java'ya özgü olmayan bir sorundur. İki şamandırası / iki katı / herhangi bir ondalık tip numarasını karşılaştırmak için == kullanmak, depolanma biçimleri nedeniyle sorunlara neden olabilir. Tek hassasiyetli bir şamandıra (IEEE standardı 754'e göre) aşağıdaki gibi dağıtılan 32 bite sahiptir:

1 bit - İşaret (0 = pozitif, 1 = negatif)
8 bit - Üstel (2 ^ x içinde x'in özel (önyargı-127) gösterimi)
23 bit - Mantisa. Kaydedilen gerçek sayı.

Soruna mantisa neden olur. Bilimsel gösterim gibi, sadece taban 2'deki (ikili) sayı 1.110011 x 2 ^ 5 veya benzer bir şeye benziyor. Ancak ikili dosyada, ilk 1 her zaman 1'dir (0'ın temsili hariç)

Bu nedenle, biraz hafıza alanı kazanmak için (pun amaçlı), IEEE 1'in kabul edilmesi gerektiğine karar verdi. Örneğin, 1011'lik bir mantis gerçekten 1.1011'dir.

Bu, karşılaştırma ile bazı sorunlara neden olabilir, özellikle 0 ile, 0 muhtemelen bir şamandırada tam olarak temsil edilemez. Bu, diğer cevaplar tarafından açıklanan kayan nokta matematik konularına ek olarak == 'ın cesaret kırılmasının ana nedenidir.

Java, dilin her biri kendi benzersiz şamandıra biçimine sahip olabilen birçok farklı platformda evrensel olması bakımından benzersiz bir soruna sahiptir. Bu == 'dan kaçınmayı daha da önemli hale getirir.

Eşitlik için iki şamandırayı (dile özgü olmayan zihin) karşılaştırmanın doğru yolu aşağıdaki gibidir:

if(ABS(float1 - float2) < ACCEPTABLE_ERROR)
    //they are approximately equal

burada ACCEPTABLE_ERROR #defined ya da Victor'un belirttiği gibi 0,000000001'e eşit veya başka bir hassasiyete ihtiyaç duyar.

Bazı diller bu işlevselliğe veya bu sabit yerleşik olarak bulunur, ancak genellikle bu iyi bir alışkanlıktır.


3
Java, kayan reklamlar için tanımlanmış bir davranışa sahiptir. Platforma bağımlı değildir.
Yishai

9

Bugün itibariyle bunu yapmanın hızlı ve kolay yolu:

if (Float.compare(sectionID, currentSectionID) == 0) {...}

Ancak dokümanlar , kenar boşluğu farkının değerini açıkça belirtmez (bir epsilon) , kayan nokta hesaplamalarında her zaman bulunan @Victor yanıtından , ancak standart dil kütüphanesinin bir parçası olduğu için makul bir şey olmalıdır.

Yine de daha yüksek veya özelleştirilmiş bir hassasiyet gerekiyorsa,

float epsilon = Float.MIN_NORMAL;  
if(Math.abs(sectionID - currentSectionID) < epsilon){...}

başka bir çözüm seçeneğidir.


1
Bağladığınız dokümanlar, "eğer f1 sayısal olarak f2'ye eşitse 0 değerini" belirtir, bu da bunu (sectionId == currentSectionId)kayan noktalar için doğru olmayan yapmakla aynı yapar . epsilon yöntemi bu cevapta daha iyi bir yaklaşımdır: stackoverflow.com/a/1088271/4212710
typoerrpr

8

Kayan nokta değerleri nedeniyle kayan nokta değerleri güvenilir değildir.

Bu nedenle, bunlar büyük olasılıkla sectionID gibi anahtar değerler olarak kullanılmamalıdır. Bunun yerine tamsayılar kullanın veya longeğer intyeterince olası değerleri içermiyor.


2
Kabul. Bunların kimlikler olduğu göz önüne alındığında, kayan nokta aritmetiği ile işleri karmaşıklaştırmak için bir neden yoktur.
Yohnny

2
Ya da uzun. Gelecekte kaç benzersiz kimliğin oluşturulacağına bağlı olarak, bir int yeterince büyük olmayabilir.
Wayne Hartman

Şamandıra ile karşılaştırıldığında çift ne kadar hassas?
Arvindh Mani

1
@ArvindhMani doubles çok daha kesindir , ama aynı zamanda kayan nokta değerleridir, bu yüzden cevabım hem floatve hem de double.
Eric Wilson

7

Önceki cevaplara ek olarak, ilişkili garip davranışlar olduğunu bilmelidir -0.0fve +0.0f(onlar ==değil equals) ve Float.NaN(öyle equalsdeğil== ) (umut doğru olduğunu var! - argh bunu yapmayın).

Düzenleme: Kontrol edelim!

import static java.lang.Float.NaN;
public class Fl {
    public static void main(String[] args) {
        System.err.println(          -0.0f   ==              0.0f);   // true
        System.err.println(new Float(-0.0f).equals(new Float(0.0f))); // false
        System.err.println(            NaN   ==               NaN);   // false
        System.err.println(new Float(  NaN).equals(new Float( NaN))); // true
    }
} 

IEEE / 754'e hoş geldiniz.


Bir şey == ise, o zaman bit ile aynıdır. Nasıl eşit olamazlar ()? Belki de geriye dönüksünüz?
mkb

@Matt NaN özeldir. Java'daki double.isNaN (double x) aslında {return x! = X; } ...
quant_dev

1
Şamandıralarda, ==sayıların "bit ile özdeş" olduğu anlamına gelmez (aynı sayı, farklı bit desenleriyle temsil edilebilir, ancak bunlardan sadece biri normalleştirilmiş formdur). Bunun yanı sıra, -0.0fve 0.0ffarklı bit kalıpları (oturum biraz farklı) ile temsil edilen, ancak eşit olarak karşılaştırılırdır ==(ancak ile equals). ==Bitsel karşılaştırma olan varsayımınız , genel olarak, yanlıştır.
Pavel Minaev


5

Float.floatToIntBits () öğesini kullanabilirsiniz.

Float.floatToIntBits(sectionID) == Float.floatToIntBits(currentSectionID)

1
Doğru yoldasın. floatToIntBits () gitmek için doğru yoldur, ancak Float'ın yerleşik equals () işlevini kullanmak daha kolay olacaktır. Buraya bakın: stackoverflow.com/a/3668105/2066079 . Varsayılan equals () işlevinin dahili olarak floatToIntBits kullandığını görebilirsiniz.
dberm22

1
Evet, bunlar Float nesneleriyse. İlkel denklemler için yukarıdaki denklemi kullanabilirsiniz.
aamadmi

4

Her şeyden önce, yüzüyorlar mı yoksa yüzüyorlar mı? Bunlardan biri bir Float ise, equals () yöntemini kullanmalısınız. Ayrıca, muhtemelen en iyi statik Float.compare yöntemini kullanmaktır.


4

Aşağıdakiler otomatik olarak en iyi hassasiyeti kullanır:

/**
 * Compare to floats for (almost) equality. Will check whether they are
 * at most 5 ULP apart.
 */
public static boolean isFloatingEqual(float v1, float v2) {
    if (v1 == v2)
        return true;
    float absoluteDifference = Math.abs(v1 - v2);
    float maxUlp = Math.max(Math.ulp(v1), Math.ulp(v2));
    return absoluteDifference < 5 * maxUlp;
}

Tabii ki, 5'den fazla veya daha az ULP ('son yerde birim') seçebilirsiniz.

Apache Commons kütüphaneye iseniz, Precisionsınıf vardır compareTo()ve equals()epsilon ve ULP ikisi ile.


şamandırayı iki katına değiştirirken, bu yöntem isDoubleEqual (0.1 + 0.2-0.3, 0.0) == false
hychou 26:17

Bunu doublekapsayacak faktör olarak 10_000_000_000_000_000L gibi daha fazlasına ihtiyacınız var gibi görünüyor .
Michael Piefel

3

== olmasını isteyebilirsiniz, ancak 123.4444444444443! = 123.4444444444442



2

Eşit gerçek sayılar üreten iki farklı hesaplama mutlaka eşit kayan nokta sayıları üretmez. Hesaplama sonuçlarını karşılaştırmak için == kullanan insanlar genellikle buna şaşırırlar, bu nedenle uyarı, aksi takdirde ince ve yeniden üretilmesi zor olabilecek şeyleri işaretlemeye yardımcı olur.


2

SectionID ve currentSectionID adlı şeyler için şamandıraları kullanacak dış kaynak koduyla mı uğraşıyorsunuz? Sadece merak.

@ Fatura K: "Bir şamandıranın ikili temsili biraz sinir bozucu." Nasıl yani? Nasıl daha iyi yapardın? Hiçbir şekilde düzgün bir şekilde temsil edilemeyen bazı sayılar vardır, çünkü asla bitmezler. Pi buna iyi bir örnek. Sadece tahmin edebilirsiniz. Daha iyi bir çözümünüz varsa Intel ile iletişim kurun.


1

Diğer cevaplarda belirtildiği gibi, çiftler küçük sapmalara sahip olabilir. Ve bunları "kabul edilebilir" bir sapma kullanarak karşılaştırmak için kendi yönteminizi yazabilirsiniz. Ancak ...

Çiftleri karşılaştırmak için bir apache sınıfı vardır: org.apache.commons.math3.util.Precision

Bazı ilginç sabitler içerir: SAFE_MINve EPSILONbasit aritmetik işlemlerin olası maksimum sapmalarıdır.

Ayrıca çiftleri karşılaştırmak, eşitlemek veya yuvarlaklaştırmak için gerekli yöntemleri sağlar. (ulps veya mutlak sapma kullanarak)


1

Bir satır cevap söyleyebilirim, kullanmalısınız:

Float.floatToIntBits(sectionID) == Float.floatToIntBits(currentSectionID)

İlgili işleçleri doğru bir şekilde kullanma hakkında daha fazla bilgi edinmek için, burada bazı durumları ayrıntılı olarak açıklarım: Genel olarak, Java'da dizeleri test etmenin üç yolu vardır. ==, .equals () veya Objects.equals () kullanabilirsiniz.

Nasıl farklılar? == Dizelerde referans kalitesi için testler, yani iki nesnenin aynı olup olmadığını bulma. Öte yandan, .equals () yöntemi, iki dizenin mantıksal olarak eşit değerde olup olmadığını test eder. Son olarak, Objects.equals () yöntemi, iki dizedeki null değerleri test eder.

Kullanmak için ideal operatör

Bu çok sayıda tartışmaya maruz kaldı çünkü üç operatörün her birinin kendine özgü güçlü ve zayıf yanları var. Örnek, == genellikle nesne başvurusunu karşılaştırırken tercih edilen bir seçenektir, ancak dize değerlerini de karşılaştırabileceği durumlar vardır.

Ancak, aldığınız şey bir düşme değeridir, çünkü Java değerleri karşılaştırdığınız bir yanılsama yaratır, ancak gerçek anlamda değildir. Aşağıdaki iki durumu düşünün:

Dava 1:

String a="Test";
String b="Test";
if(a==b) ===> true

Durum 2:

String nullString1 = null;
String nullString2 = null;
//evaluates to true
nullString1 == nullString2;
//throws an exception
nullString1.equals(nullString2);

Bu nedenle, tasarlanmış olduğu belirli bir özelliği test ederken her bir operatörü kullanmak daha iyidir. Ancak neredeyse her durumda, Objects.equals () daha evrensel bir operatördür, bu nedenle web geliştiricilerinin bunu tercih ettiğini deneyimleyin.

Burada daha fazla bilgi edinebilirsiniz: http://fluentthemes.com/use-compare-strings-java/


-2

Doğru yol

java.lang.Float.compare(float1, float2)

7
Float.compare (float1, float2) bir int döndürür, bu nedenle if durumunda float1 == float2 yerine kullanılamaz. Dahası, bu uyarının bahsettiği temel problemi gerçekten çözmez - eğer şamandıralar sayısal hesaplama sonucu ise, float1! = Float2 sadece yuvarlama hataları nedeniyle oluşabilir.
quant_dev

1
doğru, yapıştırmayı kopyalayamazsınız, önce dokümanı kontrol etmeniz gerekir.
Eric

2
Float1 == float2 yerine yapabilecekleriniz Float.compare (float1, float2) == 0.
caydırmak

29
Bu size hiçbir şey satın almaz - hala elde edersinizFloat.compare(1.1 + 2.2, 3.3) != 0
Pavel Minaev

-2

Yuvarlama hatasını azaltmanın bir yolu şamandıra yerine çift kullanmaktır. Bu, sorunu ortadan kaldırmaz, ancak programınızdaki hata miktarını azaltır ve şamandıra neredeyse hiçbir zaman en iyi seçim değildir. BENİM NACİZANE FİKRİME GÖRE.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.