C में अहस्ताक्षरित रूपांतरण पर हस्ताक्षरित - क्या यह हमेशा सुरक्षित है?


135

मान लीजिए मेरे पास निम्नलिखित C कोड है।

unsigned int u = 1234;
int i = -5678;

unsigned int result = u + i;

यहाँ क्या निहितार्थ हो रहे हैं, और क्या यह कोड सभी मूल्यों के लिए सुरक्षित है uऔर i? (सुरक्षित, इस अर्थ में कि भले ही इस उदाहरण के परिणामस्वरूप कुछ विशाल सकारात्मक संख्या में अतिप्रवाह हो जाए, मैं इसे वापस इंट में डाल सकता हूं और वास्तविक परिणाम प्राप्त कर सकता हूं ।)

जवाबों:


223

संक्षिप्त जवाब

आपका जोड़कर एक अहस्ताक्षरित पूर्णांक में परिवर्तित कर दियाi जाएगा , फिर जोड़ को अहस्ताक्षरित मानों के साथ किया जाएगा, जिसके परिणामस्वरूप एक बड़ा ( और मूल्यों के आधार पर ) होगा।UINT_MAX + 1resultui

लंबा जवाब

C99 मानक के अनुसार:

6.3.1.8 सामान्य अंकगणितीय रूपांतरण

  1. यदि दोनों ऑपरेंड का एक ही प्रकार है, तो आगे रूपांतरण की आवश्यकता नहीं है।
  2. अन्यथा, यदि दोनों ऑपरेंड ने पूर्णांक प्रकारों पर हस्ताक्षर किए हैं या दोनों के पास अहस्ताक्षरित प्रकार हैं, तो कम पूर्णांक रूपांतरण प्रकार के साथ ऑपरेंड को अधिक रैंक वाले ऑपरेंड के प्रकार में बदल दिया जाता है।
  3. अन्यथा, यदि उस ऑपरेंड ने जो अहस्ताक्षरित पूर्णांक प्रकार का रैंक किया है, वह अन्य ऑपरेटर के प्रकार के रैंक के बराबर या उससे अधिक है, तो हस्ताक्षर किए गए पूर्णांक प्रकार वाले ऑपरेटर को बिना पूर्णांक प्रकार के साथ ऑपरेटर के प्रकार में बदल दिया जाता है।
  4. अन्यथा, यदि हस्ताक्षरित पूर्णांक प्रकार के साथ ऑपरेंड का प्रकार, सभी प्रकार के मानों का प्रतिनिधित्व अहस्ताक्षरित पूर्णांक प्रकार के साथ कर सकता है, तो अहस्ताक्षरित पूर्णांक प्रकार वाला ऑपरेटर हस्ताक्षरित पूर्णांक प्रकार के साथ ऑपरेटर के प्रकार में बदल जाता है।
  5. अन्यथा, दोनों ऑपरेंड हस्ताक्षरित पूर्णांक प्रकार के साथ ऑपरेटर के प्रकार के अनुरूप अहस्ताक्षरित पूर्णांक प्रकार में परिवर्तित हो जाते हैं।

आपके मामले में, हमारे पास एक अहस्ताक्षरित int ( u) और हस्ताक्षरित int ( i) है। ऊपर (3) का संदर्भ देते हुए, चूंकि दोनों ऑपरेंड में समान रैंक है, इसलिए आपको एक अहस्ताक्षरित पूर्णांक में परिवर्तित करना iहोगा ।

6.3.1.3 हस्ताक्षरित और अहस्ताक्षरित पूर्णांक

  1. जब पूर्णांक प्रकार के साथ एक मान _Bool के अलावा किसी अन्य पूर्णांक प्रकार में परिवर्तित हो जाता है, यदि मान को नए प्रकार से दर्शाया जा सकता है, तो यह अपरिवर्तित है।
  2. अन्यथा, यदि नया प्रकार अहस्ताक्षरित है, तो मूल्य को बार-बार जोड़कर या घटाकर अधिकतम मूल्य से एक बार घटाया जाता है जो नए प्रकार में तब तक प्रस्तुत किया जा सकता है जब तक कि मूल्य नए प्रकार की सीमा में न हो।
  3. अन्यथा, नए प्रकार पर हस्ताक्षर किए गए हैं और इसमें मूल्य का प्रतिनिधित्व नहीं किया जा सकता है; या तो परिणाम कार्यान्वयन-परिभाषित है या कार्यान्वयन-परिभाषित संकेत उठाया जाता है।

अब हम ऊपर (2) को देखें। iजोड़कर आपका अहस्ताक्षरित मान में बदल दिया जाएगा UINT_MAX + 1। तो परिणाम इस बात पर निर्भर करेगा कि UINT_MAXआपके कार्यान्वयन पर कैसे परिभाषित किया गया है। यह बड़ा होगा, लेकिन यह अतिप्रवाह नहीं करेगा, क्योंकि:

6.2.5 (9)

अहस्ताक्षरित ऑपरेंड को शामिल करने वाली संगणना कभी भी ओवरफ्लो नहीं हो सकती है, क्योंकि परिणामी अहस्ताक्षरित पूर्णांक प्रकार द्वारा प्रतिनिधित्व नहीं किया जा सकता है, इसलिए मॉडुलो की संख्या कम कर दी जाती है जो कि सबसे बड़े मूल्य से अधिक है जिसे परिणामी प्रकार द्वारा दर्शाया जा सकता है।

बोनस: अंकगणित रूपांतरण सेमी-डब्ल्यूटीएफ

#include <stdio.h>

int main(void)
{
  unsigned int plus_one = 1;
  int minus_one = -1;

  if(plus_one < minus_one)
    printf("1 < -1");
  else
    printf("boring");

  return 0;
}

इस लिंक का उपयोग आप ऑनलाइन करने के लिए कर सकते हैं: https://repl.it/repls/QuickWhimsicalBytes

बोनस: अंकगणित रूपांतरण साइड इफेक्ट

अंकगणित रूपांतरण नियमों का उपयोग करने के लिए UINT_MAXएक अहस्ताक्षरित मूल्य को शुरू करने के मूल्य प्राप्त करने के लिए किया जा सकता है -1, अर्थात:

unsigned int umax = -1; // umax set to UINT_MAX

यह सिस्टम के हस्ताक्षरित संख्या प्रतिनिधित्व की परवाह किए बिना पोर्टेबल होने की गारंटी है क्योंकि ऊपर वर्णित रूपांतरण नियम हैं। अधिक जानकारी के लिए यह SO प्रश्न देखें: क्या सभी बिट्स को सही पर सेट करने के लिए -1 का उपयोग करना सुरक्षित है?


मुझे समझ में नहीं आता कि यह केवल एक निरपेक्ष मूल्य क्यों नहीं कर सकता है और फिर इलाज उतना ही अहस्ताक्षरित है, जितना सकारात्मक संख्या के साथ?
जोस साल्वेटेरिया

7
@ डी। सिंह क्या आप जवाब के भीतर गलत हिस्सों की ओर संकेत कर सकते हैं?
शमिल द कैट

अहस्ताक्षरित पर हस्ताक्षर करने के लिए, हम अहस्ताक्षरित मूल्य (UINT_MAX +1) का अधिकतम मूल्य जोड़ते हैं। इसी तरह अहस्ताक्षरित से हस्ताक्षरित में परिवर्तित करने का आसान तरीका क्या है? क्या हमें दी गई संख्या को अधिकतम मान से घटाया जाना चाहिए (अहस्ताक्षरित चार के मामले में 256)? उदाहरण के लिए: 140 जब हस्ताक्षरित संख्या में परिवर्तित हो जाता है -116। लेकिन 20 तो 20 ही बन जाता है। तो यहाँ कोई आसान ट्रिक?
जॉन व्हीकॉक


24

हस्ताक्षरित से अहस्ताक्षरित में रूपांतरण जरूरी नहीं है कि केवल हस्ताक्षरित मूल्य के प्रतिनिधित्व की प्रतिलिपि बनाएँ या फिर से व्याख्या करें। C मानक को उद्धृत करना (C99 6.3.1.3):

जब पूर्णांक प्रकार के साथ एक मान _Bool के अलावा किसी अन्य पूर्णांक प्रकार में परिवर्तित हो जाता है, यदि मान को नए प्रकार से दर्शाया जा सकता है, तो यह अपरिवर्तित है।

अन्यथा, यदि नया प्रकार अहस्ताक्षरित है, तो मूल्य को बार-बार जोड़कर या घटाकर अधिकतम मूल्य से एक बार घटाया जाता है जो नए प्रकार में तब तक प्रस्तुत किया जा सकता है जब तक कि मूल्य नए प्रकार की सीमा में न हो।

अन्यथा, नए प्रकार पर हस्ताक्षर किए गए हैं और इसमें मूल्य का प्रतिनिधित्व नहीं किया जा सकता है; या तो परिणाम कार्यान्वयन-परिभाषित है या कार्यान्वयन-परिभाषित संकेत उठाया जाता है।

इन दिनों दो के पूरक प्रतिनिधित्व के लिए, जो लगभग सार्वभौमिक है, नियम बिट्स को फिर से परिभाषित करने के अनुरूप हैं। लेकिन अन्य अभ्यावेदन (हस्ताक्षर और परिमाण या लोगों के पूरक) के लिए, सी कार्यान्वयन को अभी भी उसी परिणाम की व्यवस्था करनी चाहिए, जिसका अर्थ है कि रूपांतरण केवल बिट्स की प्रतिलिपि नहीं बना सकता है। उदाहरण के लिए, (अहस्ताक्षरित) -1 == UINT_MAX, प्रतिनिधित्व की परवाह किए बिना।

सामान्य तौर पर, C में रूपांतरणों को मूल्यों पर संचालित करने के लिए परिभाषित किया जाता है, अभ्यावेदन पर नहीं।

मूल प्रश्न का उत्तर देने के लिए:

unsigned int u = 1234;
int i = -5678;

unsigned int result = u + i;

मैं का मान अहस्ताक्षरित int, yielding में बदल जाता है UINT_MAX + 1 - 5678। यह मान तब उत्पन्न किए गए मान 1234 में जोड़ा जाता है, उपज UINT_MAX + 1 - 4444

(अहस्ताक्षरित ओवरफ़्लो के विपरीत, हस्ताक्षरित अतिप्रवाह अपरिभाषित व्यवहार को आमंत्रित करता है। रैपराउंड आम है, लेकिन सी मानक द्वारा गारंटी नहीं है - और संकलक अनुकूलन कोड पर कहर बरपा सकता है जो अनुचित धारणा बनाता है।


5

बाइबिल का जिक्र :

  • आपके जोड़ के ऑपरेशन के कारण int को अहस्ताक्षरित int में परिवर्तित किया जा सकता है।
  • दो के पूरक प्रतिनिधित्व और समान प्रकार के आकार को मानते हुए, बिट पैटर्न नहीं बदलता है।
  • अहस्ताक्षरित इंट से हस्ताक्षरित इंट में रूपांतरण कार्यान्वयन पर निर्भर है। (लेकिन यह संभवतः उन दिनों सबसे अधिक प्लेटफार्मों पर आपकी अपेक्षा के अनुरूप काम करता है।)
  • अलग-अलग आकारों के हस्ताक्षरित और अहस्ताक्षरित के संयोजन के मामले में नियम थोड़े अधिक जटिल हैं।

3

जब एक अहस्ताक्षरित और एक हस्ताक्षरित चर जोड़ा जाता है (या कोई भी बाइनरी ऑपरेशन) दोनों को अंतर्निहित रूप से अहस्ताक्षरित में बदल दिया जाता है, जो इस मामले में एक बड़ा परिणाम होगा।

इसलिए यह इस मायने में सुरक्षित है कि परिणाम बहुत बड़ा और गलत हो सकता है, लेकिन यह कभी भी दुर्घटनाग्रस्त नहीं होगा।


सच नहीं। ६.३.१. you सामान्य अंकगणितीय रूपांतरण यदि आप एक इंट और एक अहस्ताक्षरित चार को जोड़ते हैं तो बाद को इंट में बदल दिया जाता है। यदि आप दो अहस्ताक्षरित चार राशि लेते हैं, तो वे इंट में बदल जाते हैं।
2501

3

जब हस्ताक्षरित से अहस्ताक्षरित में परिवर्तित करना दो संभावनाएँ हैं। संख्या जो मूल रूप से सकारात्मक बनी हुई थी (या उसी के रूप में व्याख्या की जाती है)। संख्या जो मूल रूप से नकारात्मक थी अब बड़ी सकारात्मक संख्याओं के रूप में व्याख्या की जाएगी।


1

जैसा कि पहले उत्तर दिया गया था, आप बिना किसी समस्या के हस्ताक्षरित और बिना हस्ताक्षर के बीच आगे-पीछे कर सकते हैं। हस्ताक्षर किए पूर्णांक के लिए सीमा मामला -1 (0xFFFFFFFF) है। उससे जोड़ने और घटाने की कोशिश करें और आप पाएंगे कि आप वापस जा सकते हैं और यह सही हो सकता है।

हालांकि, यदि आप आगे और पीछे कास्टिंग करने जा रहे हैं, तो मैं दृढ़ता से आपके चर का नामकरण करने की सलाह दूंगा, ताकि यह स्पष्ट हो सके कि वे किस प्रकार के हैं, जैसे:

int iValue, iResult;
unsigned int uValue, uResult;

यह अधिक महत्वपूर्ण मुद्दों से विचलित होने के लिए बहुत आसान है और यह भूल जाते हैं कि किस चर को बिना संकेत के नाम दिया गया है। आप एक अहस्ताक्षरित को कास्ट नहीं करना चाहते हैं और फिर एक सरणी सूचकांक के रूप में उपयोग करते हैं।


0

यहाँ क्या निहितार्थ हो रहे हैं,

मैं एक अहस्ताक्षरित पूर्णांक में बदल दिया जाएगा।

और क्या यह कोड यू और आई के सभी मूल्यों के लिए सुरक्षित है?

अच्छी तरह से परिभाषित हाँ के अर्थ में सुरक्षित (देखें) https://stackoverflow.com/a/50632/5083516 )।

नियमों को आमतौर पर मानकों को पढ़ने के लिए मुश्किल से लिखा जाता है, लेकिन अनिवार्य रूप से हस्ताक्षरित पूर्णांक में जो भी प्रतिनिधित्व का उपयोग किया गया था, उसमें अहस्ताक्षरित पूर्णांक में संख्या का 2 का प्रतिनिधित्व प्रतिनिधित्व होगा।

जोड़, घटाव और गुणा इन नंबरों पर सही ढंग से काम करेगा जिसके परिणामस्वरूप एक और अहस्ताक्षरित पूर्णांक होता है जिसमें "वास्तविक परिणाम" का प्रतिनिधित्व करने वाले एक ट्वोज़ पूरक संख्या होती है।

विभाजन और बड़े अहस्ताक्षरित प्रकारों के लिए कास्टिंग में अच्छी तरह से परिभाषित परिणाम होंगे, लेकिन वे परिणाम "सफलतम" के 2 पूरक प्रतिनिधित्व नहीं होंगे।

(सुरक्षित, इस अर्थ में कि भले ही इस उदाहरण में परिणाम कुछ विशाल सकारात्मक संख्या तक पहुंच जाएगा, मैं इसे वापस एक अंतर में डाल सकता हूं और वास्तविक परिणाम प्राप्त कर सकता हूं।)

जब हस्ताक्षर किए गए बिना अहस्ताक्षरित से रूपांतरण मानक द्वारा परिभाषित किया जाता है, तो रिवर्स कार्यान्वयन कार्यान्वयन-परिभाषित है gcc और msvc दोनों रूपांतरण को इस तरह परिभाषित करते हैं कि जब आप हस्ताक्षरित पूर्णांक में वापस एक अहस्ताक्षरित पूर्णांक में संग्रहीत 2 के पूरक नंबर को परिवर्तित करते हैं तो आपको "वास्तविक परिणाम" मिलेगा। । मुझे उम्मीद है कि आपको केवल अस्पष्ट सिस्टम पर कोई अन्य व्यवहार मिलेगा जो हस्ताक्षरित पूर्णांक के लिए 2 के पूरक का उपयोग नहीं करता है।

https://gcc.gnu.org/onbuildocs/gcc/Integers-implementation.html#Integers-implementation https://msdn.microsoft.com/en-us/library/0eex498h.aspx


-17

भयानक जवाब Galore

ओजगुर ओज़ितक

जब आपने हस्ताक्षरित से अहस्ताक्षरित (और इसके विपरीत) में डाला तो संख्या का आंतरिक प्रतिनिधित्व नहीं बदलता है। क्या परिवर्तन होता है संकलक साइन बिट की व्याख्या कैसे करता है।

यह पूरी तरह से गलत है।

मैट फ्रेड्रिक्सन

जब एक अहस्ताक्षरित और एक हस्ताक्षरित चर जोड़ा जाता है (या कोई भी बाइनरी ऑपरेशन) दोनों को अंतर्निहित रूप से अहस्ताक्षरित में बदल दिया जाता है, जो इस मामले में एक बड़ा परिणाम होगा।

यह भी गलत है। अहस्ताक्षरित प्रकार को बढ़ावा दिया जा सकता है, जिस प्रकार के प्रिंट में बिट्स को अविशिष्ट प्रकार में पैडिंग बिट्स के कारण समान परिशुद्धता होना चाहिए।

smh

आपके जोड़ के ऑपरेशन के कारण int को अहस्ताक्षरित int में परिवर्तित किया जा सकता है।

गलत। शायद यह करता है और शायद यह नहीं है।

अहस्ताक्षरित इंट से हस्ताक्षरित इंट में रूपांतरण कार्यान्वयन पर निर्भर है। (लेकिन यह संभवतः उन दिनों सबसे अधिक प्लेटफार्मों पर आपकी अपेक्षा के अनुरूप काम करता है।)

गलत। यह या तो अपरिभाषित व्यवहार है अगर यह अतिप्रवाह का कारण बनता है या मूल्य संरक्षित है।

गुमनाम

मैं का मान अहस्ताक्षरित int में बदल जाता है ...

गलत। एक अहस्ताक्षरित int के सापेक्ष एक int की सटीकता पर निर्भर करता है।

टेलर मूल्य

जैसा कि पहले उत्तर दिया गया था, आप बिना किसी समस्या के हस्ताक्षरित और बिना हस्ताक्षर के बीच आगे-पीछे कर सकते हैं।

गलत। अपरिभाषित व्यवहार में एक हस्ताक्षरित पूर्णांक परिणामों की सीमा के बाहर एक मूल्य को संग्रहीत करने की कोशिश करना।

अब मैं आखिरकार सवाल का जवाब दे सकता हूं।

Int की सटीकता अहस्ताक्षरित int के बराबर होनी चाहिए, u को एक हस्ताक्षरित int में पदोन्नत किया जाएगा और आपको अभिव्यक्ति (u + i) से -4444 मिलेगा। अब, क्या आपके पास अन्य मूल्य हैं, आपको अतिप्रवाह और अपरिभाषित व्यवहार मिल सकता है, लेकिन उन सटीक संख्याओं के साथ आपको -4444 [1] मिलेगा । इस मान का प्रकार int होगा। लेकिन आप उस मान को एक अहस्ताक्षरित int में संग्रहीत करने की कोशिश कर रहे हैं, ताकि फिर एक अहस्ताक्षरित int में डाल दिया जाए और जो परिणाम समाप्त हो जाएगा, वह (UINT_MAX + 1) - 4444 होगा।

क्या अहस्ताक्षरित int की सटीकता एक इंट की तुलना में अधिक होनी चाहिए, हस्ताक्षरित int को एक अहस्ताक्षरित int येल्डिंग वैल्यू (UINT_MAX + 1) में बढ़ावा दिया जाएगा - 5678 जो अन्य अहस्ताक्षरित int 1234 में जोड़ा जाएगा क्या आपके पास u और i होना चाहिए अन्य मान, जो अभिव्यक्ति को सीमा के बाहर गिरते हैं {0..UINT_MAX} मान (UINT_MAX + 1) या तो जोड़ा या घटाया जाएगा जब तक कि परिणाम सीमा के अंदर नहीं आते {0..UINT_MAX) और कोई अपरिभाषित व्यवहार नहीं होगा ।

परिशुद्धता क्या है?

इंटेगर में पैडिंग बिट्स, साइन बिट्स और वैल्यू बिट्स होते हैं। निरुपित पूर्णांक स्पष्ट रूप से एक संकेत बिट नहीं है। Unsigned char को पैडिंग बिट्स नहीं करने की गारंटी है। एक पूर्णांक बिट्स की संख्या में मान होता है कि इसमें कितनी सटीकता है।

[Gotchas]

यदि पैडिंग बिट्स मौजूद हैं, तो पूर्णांक आकार की मैक्रो का उपयोग पूर्णांक की सटीकता को निर्धारित करने के लिए नहीं किया जा सकता है। और बाइट का आकार ऑक्टेट (आठ बिट्स) होना जरूरी नहीं है जैसा कि C99 द्वारा परिभाषित किया गया है।

[१] अतिप्रवाह दो बिंदुओं में से एक पर हो सकता है। या तो इसके अलावा (पदोन्नति के दौरान) - जब आपके पास एक अहस्ताक्षरित int है जो कि एक int के अंदर फिट होने के लिए बहुत बड़ा है। ओवरफ्लो भी हो सकता है इसके अलावा अगर अहस्ताक्षरित इंट की सीमा के भीतर था, तो इसके अलावा परिणाम अभी भी अतिप्रवाह हो सकता है।


6
"अनसाइन्टेड इनट्स को किलों में बढ़ावा दिया जा सकता है"। सच नहीं। कोई पूर्णांक प्रचार तब नहीं होता है जब प्रकार पहले से ही रैंक> = int हैं। 6.3.1.1: "किसी भी अहस्ताक्षरित पूर्णांक प्रकार की रैंक संबंधित हस्ताक्षरित पूर्णांक प्रकार के रैंक के बराबर होगी, यदि कोई हो।" और 6.3.1.8: "अन्यथा, यदि पूर्णांक प्रकार वाले ऑपरेटर के पास अन्य ऑपरेटर के प्रकार के रैंक के बराबर या उससे अधिक रैंक है, तो हस्ताक्षर किए गए पूर्णांक प्रकार वाले ऑपरेटर को बिना पूर्णांक वाले ऑपरेटर के प्रकार में बदल दिया जाता है। प्रकार।" दोनों की गारंटी है कि जब सामान्य अंकगणितीय रूपांतरण लागू होते हैं तो intपरिवर्तित हो unsigned intजाते हैं।
सीबी बेली

1
6.3.1.8 पूर्णांक पदोन्नति के बाद ही होता है। ओपनिंग पैराग्राफ कहता है "अन्यथा, पूर्णांक पदोन्नति दोनों ऑपरेंड पर की जाती है। निम्नलिखित नियमों को पदोन्नत ऑपरेंड पर लागू किया जाता है"। तो प्रमोशन नियमों को पढ़ें 6.3.1.1 ... "एक पूर्णांक प्रकार के साथ एक वस्तु या अभिव्यक्ति जिसका पूर्णांक रूपांतरण रैंक इंटैक और अहस्ताक्षरित इंट के रैंक से कम या एक से अधिक है" और "यदि कोई इंट सभी मानों का प्रतिनिधित्व कर सकता है" मूल प्रकार, मान को एक इंट में बदल दिया जाता है "।
अभिजात वर्ग Mx

1
6.3.1.1 पूर्णांक पदोन्नति कुछ पूर्णांक प्रकार है कि नहीं कर रहे हैं परिवर्तित करने के लिए इस्तेमाल किया करते थे intया unsigned intउन प्रकार जहां प्रकार के कुछ से एक के लिए unsigned intया intकी उम्मीद है। "या बराबर" TC2 में जोड़ा गया रूपांतरण रैंक के प्रगणित प्रकार अनुमति देने के लिए के बराबर intया unsigned intउन प्रकार से एक के लिए परिवर्तित किया। यह कभी भी इरादा नहीं था कि वर्णित पदोन्नति unsigned intऔर के बीच में परिवर्तित हो जाएगी int। के बीच सामान्य प्रकार का निर्धारण unsigned intऔर intअभी भी 6.3.1.8 द्वारा शासित है, यहां तक ​​कि टीसी 2 भी पोस्ट करता है।
सीबी बेली

19
दूसरों के गलत जवाबों की आलोचना करते हुए गलत जवाब पोस्ट करना ... काम पाने के लिए एक अच्छी रणनीति की तरह नहीं लगता ... ;-)
R .. GitHub STOP HELPING ICE

6
मैं हटाने के लिए मतदान नहीं कर रहा हूँ क्योंकि अहंकार के साथ संयुक्त इस स्तर की गलती बहुत मनोरंजक है
MM
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.